Jelajahi Sumber

A bunch of video related changes

Billy Barrow 2 tahun lalu
induk
melakukan
e72731f431

+ 7 - 3
src/Editor.vala

@@ -274,14 +274,18 @@ namespace Publicate {
             }
             
             var stream = yield file.read_async(Priority.DEFAULT, null);
-            var compression = new Ppub.CompressionInfo(stream, false);
-            stream.seek(0, SeekType.SET, null);
             var sample = new uint8[2048];
             size_t sample_size;
             yield stream.read_all_async(sample, Priority.DEFAULT, null, out sample_size);
             stream.seek(0, SeekType.SET, null);
-
             var mimetype = Ppub.guess_mimetype(file.get_basename(), sample);
+            var compression_policy = Ppub.CompressionPolicy.AUTO;
+            if(mimetype.has_prefix("video/")) {
+                compression_policy = Ppub.CompressionPolicy.NEVER_COMPRESS;
+            }
+            var compression = new Ppub.CompressionInfo(stream, compression_policy);
+            stream.seek(0, SeekType.SET, null);
+
             yield add_asset(file.get_basename(), mimetype, stream, compression);
         }
 

+ 5 - 1
src/Savable.vala

@@ -16,7 +16,11 @@ namespace Publicate {
 
         public void save_asset(Ppub.Builder builder) throws Error {
             var stream = publication.read_asset(asset.name);
-            var compression = new Ppub.CompressionInfo(stream);
+            var compression_policy = Ppub.CompressionPolicy.AUTO;
+            if(asset.mimetype.has_prefix("video/")) {
+                compression_policy = Ppub.CompressionPolicy.NEVER_COMPRESS;
+            }
+            var compression = new Ppub.CompressionInfo(stream, compression_policy);
             stream = publication.read_asset(asset.name);
 
             builder.add_asset(asset.name, asset.mimetype, stream, Invercargill.empty<string>(), compression);

+ 2 - 1
src/Video/Encoder.vala

@@ -49,7 +49,8 @@ namespace Publicate.Video {
                 file_name = profile.output_name(),
                 codecs = profile.codec_names,
                 size = profile.size,
-                version_label = profile.version_label()
+                version_label = profile.version_label(),
+                alternative_to = profile.alternative_to?.version_label(),
             };
         }
 

+ 30 - 0
src/Video/EncodingProfile.vala

@@ -6,6 +6,7 @@ namespace Publicate.Video {
         public string size { get; protected set; }
         public string codec_names { get; protected set; }
         public double fps { get; protected set; }
+        public EncodingProfile? alternative_to { get; set; }
 
         protected double get_low_framerate(double input_framerate) {
             if(input_framerate >= 50) {
@@ -26,4 +27,33 @@ namespace Publicate.Video {
 
     }
 
+    public static Invercargill.Enumerable<EncodingProfile> construct_free_profiles() {
+        return Invercargill.ate(new EncodingProfile[] {
+            new Vp9Video1080pHighFramerateProfile (),
+            new Vp9Video1080pLowFramerateProfile (),
+            new Vp9Video720pProfile(),
+            new Vp9Video480pProfile(),
+            new TheoraVideo360pProfile(),
+            new TheoraVideo240pProfile(),
+        });
+    }
+
+    public static Invercargill.Enumerable<EncodingProfile> construct_all_profiles() {
+        var vp9_720 = new Vp9Video720pProfile();
+        var vp9_480 = new Vp9Video480pProfile();
+        var avc_720 = new H264Video720pProfile() {alternative_to = vp9_720};
+        var avc_480 = new H264Video480pProfile() {alternative_to = vp9_480};
+
+        return Invercargill.ate(new EncodingProfile[] {
+            new Vp9Video1080pHighFramerateProfile (),
+            new Vp9Video1080pLowFramerateProfile (),
+            vp9_720,
+            avc_720,
+            vp9_480,
+            avc_480,
+            new TheoraVideo360pProfile(),
+            new TheoraVideo240pProfile(),
+        });
+    }
+
 }

+ 100 - 0
src/Video/H264Profiles.vala

@@ -0,0 +1,100 @@
+namespace Publicate.Video {
+
+    public class H264Video720pProfile : EncodingProfile {
+        
+        public override string[] get_first_pass_command (string input_path, string output_path) {
+            return get_command (input_path, output_path, 1);
+        }
+        public override string[] get_second_pass_command (string input_path, string output_path) {
+            return get_command (input_path, output_path, 2);
+        }
+        public override string output_name () {
+            return @"720p.mp4";
+        }
+        public override string version_label (){
+            return @"720p H.264";
+        }
+        public override bool suitable_for (VideoInfo info) {
+            return info.height >= 720;
+        }
+        public override void setup_for (VideoInfo info) {
+            codec_names = "avc1.64001f, mp4a.40.2";
+            var width = (int) (info.ratio_frac * 720);
+            size = @"$(width)x720";
+            fps = get_low_framerate (info.frame_rate);
+        }
+
+        private string[] get_command(string input_path, string output_path, int pass) {
+            return new string[] {
+                "ffmpeg",
+                "-i", input_path,
+                "-r", fps.to_string(),
+                "-vf", @"scale=$(size)",
+                "-pix_fmt", "yuv420p",
+                "-profile:v", "baseline",
+                "-movflags", "+faststart",
+                "-b:v", "1024k",
+                "-minrate", "512k",
+                "-maxrate", "1485k",
+                "-threads", get_num_processors().to_string(),
+                "-preset", "slow",
+                "-crf", "33",
+                "-c:v", "libx264",
+                "-c:a", "aac",
+                "-pass", pass.to_string(),
+                "-passlogfile", @"$(output_path).logfile",
+                "-y",
+                output_path
+            };
+        }
+    }
+
+    public class H264Video480pProfile : EncodingProfile {
+        
+        public override string[] get_first_pass_command (string input_path, string output_path) {
+            return get_command (input_path, output_path, 1);
+        }
+        public override string[] get_second_pass_command (string input_path, string output_path) {
+            return get_command (input_path, output_path, 2);
+        }
+        public override string output_name () {
+            return @"480p.mp4";
+        }
+        public override string version_label (){
+            return @"480p H.264";
+        }
+        public override bool suitable_for (VideoInfo info) {
+            return info.height >= 720;
+        }
+        public override void setup_for (VideoInfo info) {
+            codec_names = "avc1.64001f, mp4a.40.2";
+            var width = (int) (info.ratio_frac * 720);
+            size = @"$(width)x480";
+            fps = get_low_framerate (info.frame_rate);
+        }
+
+        private string[] get_command(string input_path, string output_path, int pass) {
+            return new string[] {
+                "ffmpeg",
+                "-i", input_path,
+                "-r", fps.to_string(),
+                "-vf", @"scale=$(size)",
+                "-pix_fmt", "yuv420p",
+                "-profile:v", "baseline",
+                "-movflags", "+faststart",
+                "-b:v", "750k",
+                "-minrate", "375k",
+                "-maxrate", "1088k",
+                "-threads", get_num_processors().to_string(),
+                "-preset", "slow",
+                "-crf", "34",
+                "-c:v", "libx264",
+                "-c:a", "aac",
+                "-pass", pass.to_string(),
+                "-passlogfile", @"$(output_path).logfile",
+                "-y",
+                output_path
+            };
+        }
+    }
+}

+ 0 - 7
src/Video/TheoraProfiles.vala

@@ -1,13 +1,6 @@
 
 namespace Publicate.Video {
 
-    public static Invercargill.Enumerable<EncodingProfile> construct_theora_profiles() {
-        return Invercargill.ate(new EncodingProfile[] {
-            new TheoraVideo360pProfile (),
-            new TheoraVideo240pProfile (),
-        });
-    }
-
     public class TheoraVideo360pProfile : EncodingProfile {
         
         public override string[] get_first_pass_command (string input_path, string output_path) {

+ 6 - 2
src/Video/VideoProcessor.vala

@@ -35,7 +35,7 @@ namespace Publicate.Video {
 
         }
 
-        public async VideoProcessResult? process_video(string path, ViewerWindow window) {
+        public async VideoProcessResult? process_video(string path, ViewerWindow window, bool use_nonfree_codecs = false) {
 
             this.modal = true;
             this.transient_for = window;
@@ -45,7 +45,11 @@ namespace Publicate.Video {
             var video_info = new VideoInfo(video_file);
             yield video_info.read_info();
 
-            var profiles = construct_vp9_profiles().concat(construct_theora_profiles());
+            var profiles = construct_free_profiles();
+            if(use_nonfree_codecs) {
+                profiles = construct_all_profiles();
+            }
+
             var use_profiles = profiles.where(p => p.suitable_for(video_info));
 
             use_profiles.iterate(p => p.setup_for(video_info));

+ 0 - 9
src/Video/Vp9Profiles.vala

@@ -1,15 +1,6 @@
 
 namespace Publicate.Video {
 
-    public static Invercargill.Enumerable<EncodingProfile> construct_vp9_profiles() {
-        return Invercargill.ate(new EncodingProfile[] {
-            new Vp9Video1080pHighFramerateProfile (),
-            new Vp9Video1080pLowFramerateProfile (),
-            new Vp9Video720pProfile(),
-            new Vp9Video480pProfile(),
-        });
-    }
-
     public class Vp9Video1080pHighFramerateProfile : EncodingProfile {
         
         public override string[] get_first_pass_command (string input_path, string output_path) {

+ 62 - 5
src/Wizards/Video.vala

@@ -10,6 +10,7 @@ namespace Publicate.Wizards {
         private EntryRow author;
         private EntryRow author_email;
         private ThumbnailChooserRow thumbnail_file;
+        private ProprietaryCodecsRow proprietary_codecs;
 
         private ViewerWindow window;
 
@@ -48,6 +49,9 @@ namespace Publicate.Wizards {
             author_email.title = "Author Email";
             group.add(author_email);
 
+            proprietary_codecs = new ProprietaryCodecsRow(window);
+            group.add(proprietary_codecs);
+
             append(group);
 
 
@@ -98,7 +102,7 @@ namespace Publicate.Wizards {
             print(metadata.to_string());
 
             var processor = new Video.VideoProcessor();
-            var result = yield processor.process_video(video_file.selected_file.get_path(), window);
+            var result = yield processor.process_video(video_file.selected_file.get_path(), window, proprietary_codecs.use_proprietary_codecs);
 
             result.manifest.author = metadata.author;
             result.manifest.title = metadata.title;
@@ -129,12 +133,12 @@ namespace Publicate.Wizards {
             builder.add_asset("video_description.md", "text/markdown", description_stream, Invercargill.empty<string>(), compression_info);
 
             if(thumbnail_file.selected_file != null) {
-                yield add_file(builder, thumbnail_file.selected_file);
+                yield add_file(builder, thumbnail_file.selected_file, Ppub.CompressionPolicy.AUTO);
                 print("Added file\n");
             }
 
             foreach (var result_file in result.video_files) {
-                yield add_file(builder, result_file);
+                yield add_file(builder, result_file, Ppub.CompressionPolicy.NEVER_COMPRESS);
                 print("Added video file\n");
             }
 
@@ -154,9 +158,9 @@ namespace Publicate.Wizards {
             video_file.reset();
         }
 
-        private static async void add_file(Ppub.Builder builder, File file) throws Error {
+        private static async void add_file(Ppub.Builder builder, File file, Ppub.CompressionPolicy compression_policy) throws Error {
             var stream = yield file.read_async(Priority.DEFAULT, null);
-            var compression = new Ppub.CompressionInfo(stream, false);
+            var compression = new Ppub.CompressionInfo(stream, compression_policy);
             stream.seek(0, SeekType.SET, null);
             var sample = new uint8[2048];
             size_t sample_size;
@@ -291,4 +295,57 @@ namespace Publicate.Wizards {
 
     }
 
+    private class ProprietaryCodecsRow : ActionRow {
+        public bool use_proprietary_codecs { get; private set; }
+        private Switch gtk_switch;
+        private ViewerWindow toplevel;
+
+        public ProprietaryCodecsRow(ViewerWindow window) {
+            toplevel = window;
+            gtk_switch = new Switch();
+            gtk_switch.notify["active"].connect(confirm_change);
+            gtk_switch.vexpand = false;
+            gtk_switch.valign = Align.CENTER;
+            add_suffix(gtk_switch);
+            reset();
+
+            activatable_widget = gtk_switch;
+            activatable = true;
+
+            title = "Use Proprietary Codecs";
+            subtitle = "Compatible with more devices";
+        }
+
+        public void confirm_change() {
+            if(!gtk_switch.active) {
+                use_proprietary_codecs = false;
+                return;
+            }
+            var prompt = new Adw.MessageDialog(toplevel, "Enable Proprietary Codecs?", "This will enable the H.264 codec which is needed for this PPUB to be playable on iOS Safari, but there may be restrictive patient licences that apply to its use.");
+            prompt.add_response("info", "Read More");
+            prompt.add_response("cancel", "Cancel");
+            prompt.add_response("enable", "Enable");
+            prompt.response.connect(r => {
+                if(r == "info") {
+                    var uri = Uri.parse("https://www.fsf.org/licensing/h264-patent-license", UriFlags.PARSE_RELAXED);
+                    var app = AppInfo.get_default_for_uri_scheme(uri.get_scheme());
+                    var uris = new List<string>();
+                    uris.append(uri.to_string());        
+                    app.launch_uris(uris, null);
+                }
+                if(r != "enable") {
+                    gtk_switch.active = false;
+                }
+                prompt.close();
+                use_proprietary_codecs = gtk_switch.active;
+            });
+            prompt.present();
+        }
+
+        public void reset() {
+            use_proprietary_codecs = false;
+            gtk_switch.active = false;
+        }
+    }
+
 }

+ 1 - 0
src/meson.build

@@ -27,6 +27,7 @@ sources += files('Video/VideoInfo.vala')
 sources += files('Video/EncodingProfile.vala')
 sources += files('Video/TheoraProfiles.vala')
 sources += files('Video/Vp9Profiles.vala')
+sources += files('Video/H264Profiles.vala')
 sources += files('Video/Encoder.vala')
 sources += files('Licences/Licence.vala')
 sources += files('Licences/AllRightsReserved.vala')