Added support for the WEBM/VP9 video codec
authorSybren A. Stüvel <sybren@stuvel.eu>
Mon, 9 Apr 2018 13:27:11 +0000 (15:27 +0200)
committerSybren A. Stüvel <sybren@stuvel.eu>
Mon, 9 Apr 2018 13:27:11 +0000 (15:27 +0200)
WEBM is the codec name, and VP9 is the encoder (the older encoder "VP8"
is less efficient than VP9).

WEBM/VP9 and h.264 both have options to control the file size versus
compression time (e.g. fast but big, or slow and small, for the same
output quality). Since WEBM/VP9 only has three choices, I've chosen to
map those to 3 of the 9 possible choices of h.264:

- BEST → SLOWER
- GOOD → MEDIUM
- REALTIME → SUPERFAST

The VERYSLOW and ULTRAFAST options give very little extra benefit.

Reviewed by: @Severin

release/scripts/startup/bl_ui/properties_render.py
source/blender/blenkernel/intern/writeffmpeg.c
source/blender/blenloader/intern/versioning_270.c
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/intern/rna_scene.c

index 32f0169..a5ce345 100644 (file)
@@ -464,7 +464,7 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel):
             layout.prop(ffmpeg, "use_lossless_output")
 
         # Output quality
-        use_crf = needs_codec and ffmpeg.codec in {'H264', 'MPEG4'}
+        use_crf = needs_codec and ffmpeg.codec in {'H264', 'MPEG4', 'WEBM'}
         if use_crf:
             layout.prop(ffmpeg, "constant_rate_factor")
 
index aa81b39..d7fcd89 100644 (file)
@@ -582,24 +582,33 @@ static AVStream *alloc_video_stream(FFMpegContext *context, RenderData *rd, int
        }
 
        if (context->ffmpeg_preset) {
-               char const *preset_name;
+               /* 'preset' is used by h.264, 'deadline' is used by webm/vp9. I'm not
+                * setting those properties conditionally based on the video codec,
+                * as the FFmpeg encoder simply ignores unknown settings anyway. */
+               char const *preset_name = NULL;  /* used by h.264 */
+               char const *deadline_name = NULL; /* used by webm/vp9 */
                switch (context->ffmpeg_preset) {
-                       case FFM_PRESET_ULTRAFAST: preset_name = "ultrafast"; break;
-                       case FFM_PRESET_SUPERFAST: preset_name = "superfast"; break;
-                       case FFM_PRESET_VERYFAST: preset_name = "veryfast"; break;
-                       case FFM_PRESET_FASTER: preset_name = "faster"; break;
-                       case FFM_PRESET_FAST: preset_name = "fast"; break;
-                       case FFM_PRESET_MEDIUM: preset_name = "medium"; break;
-                       case FFM_PRESET_SLOW: preset_name = "slow"; break;
-                       case FFM_PRESET_SLOWER: preset_name = "slower"; break;
-                       case FFM_PRESET_VERYSLOW: preset_name = "veryslow"; break;
+                       case FFM_PRESET_GOOD:
+                               preset_name = "medium";
+                               deadline_name = "good";
+                               break;
+                       case FFM_PRESET_BEST:
+                               preset_name = "slower";
+                               deadline_name = "best";
+                               break;
+                       case FFM_PRESET_REALTIME:
+                               preset_name = "superfast";
+                               deadline_name = "realtime";
+                               break;
                        default:
                                printf("Unknown preset number %i, ignoring.\n", context->ffmpeg_preset);
-                               preset_name = NULL;
                }
                if (preset_name != NULL) {
                        av_dict_set(&opts, "preset", preset_name, 0);
                }
+               if (deadline_name != NULL) {
+                       av_dict_set(&opts, "deadline", deadline_name, 0);
+               }
        }
 
 #if 0
@@ -1676,7 +1685,7 @@ void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf)
                {
                        BKE_ffmpeg_preset_set(rd, FFMPEG_PRESET_H264);
                        rd->ffcodecdata.constant_rate_factor = FFM_CRF_MEDIUM;
-                       rd->ffcodecdata.ffmpeg_preset = FFM_PRESET_MEDIUM;
+                       rd->ffcodecdata.ffmpeg_preset = FFM_PRESET_GOOD;
                        rd->ffcodecdata.type = FFMPEG_MKV;
                }
                if (rd->ffcodecdata.type == FFMPEG_OGG) {
index f1c40aa..e9de791 100644 (file)
@@ -1796,6 +1796,23 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
                                }
                        }
                }
+
+               for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
+                       int preset = scene->r.ffcodecdata.ffmpeg_preset;
+                       if (preset == FFM_PRESET_NONE || preset >= FFM_PRESET_GOOD) {
+                               continue;
+                       }
+                       if (preset <= FFM_PRESET_FAST) {
+                               preset = FFM_PRESET_REALTIME;
+                       }
+                       else if (preset >= FFM_PRESET_SLOW) {
+                               preset = FFM_PRESET_BEST;
+                       }
+                       else {
+                               preset = FFM_PRESET_GOOD;
+                       }
+                       scene->r.ffcodecdata.ffmpeg_preset = preset;
+               }
        }
 }
 
index 9a14f2e..d2b71c7 100644 (file)
@@ -101,17 +101,28 @@ typedef struct AviCodecData {
 
 typedef enum eFFMpegPreset {
        FFM_PRESET_NONE,
-       FFM_PRESET_ULTRAFAST,
-       FFM_PRESET_SUPERFAST,
-       FFM_PRESET_VERYFAST,
-       FFM_PRESET_FASTER,
-       FFM_PRESET_FAST,
-       FFM_PRESET_MEDIUM,
-       FFM_PRESET_SLOW,
-       FFM_PRESET_SLOWER,
-       FFM_PRESET_VERYSLOW,
-} eFFMpegPreset;
 
+#ifdef DNA_DEPRECATED
+       /* Previously used by h.264 to control encoding speed vs. file size. */
+       FFM_PRESET_ULTRAFAST, /* DEPRECATED */
+       FFM_PRESET_SUPERFAST, /* DEPRECATED */
+       FFM_PRESET_VERYFAST,  /* DEPRECATED */
+       FFM_PRESET_FASTER,    /* DEPRECATED */
+       FFM_PRESET_FAST,      /* DEPRECATED */
+       FFM_PRESET_MEDIUM,    /* DEPRECATED */
+       FFM_PRESET_SLOW,      /* DEPRECATED */
+       FFM_PRESET_SLOWER,    /* DEPRECATED */
+       FFM_PRESET_VERYSLOW,  /* DEPRECATED */
+#endif
+
+       /* Used by WEBM/VP9 and h.264 to control encoding speed vs. file size.
+        * WEBM/VP9 use these values directly, whereas h.264 map those to
+        * respectively the MEDIUM, SLOWER, and SUPERFAST presets.
+       */
+       FFM_PRESET_GOOD = 10, /* the default and recommended for most applications */
+       FFM_PRESET_BEST, /* recommended if you have lots of time and want the best compression efficiency */
+       FFM_PRESET_REALTIME, /* recommended for live / fast encoding */
+} eFFMpegPreset;
 
 /* Mapping from easily-understandable descriptions to CRF values.
  * Assumes we output 8-bit video. Needs to be remapped if 10-bit
index 532dd03..2eedd9a 100644 (file)
@@ -5456,19 +5456,18 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
                {AV_CODEC_ID_PNG, "PNG", 0, "PNG", ""},
                {AV_CODEC_ID_QTRLE, "QTRLE", 0, "QT rle / QT Animation", ""},
                {AV_CODEC_ID_THEORA, "THEORA", 0, "Theora", ""},
+               {AV_CODEC_ID_VP9, "WEBM", 0, "WEBM / VP9", ""},
                {0, NULL, 0, NULL, NULL}
        };
 
+       /* Recommendations come from the FFmpeg wiki, https://trac.ffmpeg.org/wiki/Encode/VP9.
+        * The label for BEST has been changed to "Slowest" so that it fits the "Encoding Speed"
+        * property label in the UI. */
        static const EnumPropertyItem ffmpeg_preset_items[] = {
-               {FFM_PRESET_ULTRAFAST, "ULTRAFAST", 0, "Ultra fast; biggest file", ""},
-               {FFM_PRESET_SUPERFAST, "SUPERFAST", 0, "Super fast", ""},
-               {FFM_PRESET_VERYFAST, "VERYFAST", 0, "Very fast", ""},
-               {FFM_PRESET_FASTER, "FASTER", 0, "Faster", ""},
-               {FFM_PRESET_FAST, "FAST", 0, "Fast", ""},
-               {FFM_PRESET_MEDIUM, "MEDIUM", 0, "Medium speed", ""},
-               {FFM_PRESET_SLOW, "SLOW", 0, "Slow", ""},
-               {FFM_PRESET_SLOWER, "SLOWER", 0, "Slower", ""},
-               {FFM_PRESET_VERYSLOW, "VERYSLOW", 0, "Very slow; smallest file", ""},
+               {FFM_PRESET_BEST, "BEST", 0, "Slowest",
+                "Recommended if you have lots of time and want the best compression efficiency"},
+               {FFM_PRESET_GOOD, "GOOD", 0, "Good", "The default and recommended for most applications"},
+               {FFM_PRESET_REALTIME, "REALTIME", 0, "Realtime", "Recommended for fast encoding"},
                {0, NULL, 0, NULL, NULL}
        };
 
@@ -5604,7 +5603,7 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
        RNA_def_property_enum_bitflag_sdna(prop, NULL, "ffmpeg_preset");
        RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
        RNA_def_property_enum_items(prop, ffmpeg_preset_items);
-       RNA_def_property_enum_default(prop, FFM_PRESET_MEDIUM);
+       RNA_def_property_enum_default(prop, FFM_PRESET_GOOD);
        RNA_def_property_ui_text(prop, "Encoding speed",
                                 "Tradeoff between encoding speed and compression ratio");
        RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);