Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@stuvel.eu>2016-09-21 16:01:51 +0300
committerSybren A. Stüvel <sybren@stuvel.eu>2016-09-21 16:03:11 +0300
commita7e74791221e2ef9b44ee1b3eb9ece37785aa62a (patch)
treebdef7bcac914ea1330046baac05c52719c34cd7e
parent2476faebd751fe7a250d7a496a1f56338b83d4e9 (diff)
FFmpeg interface improvements
This patch changes a couple of things in the video output encoding. {F362527} - Clearer separation between container and codec. No more "format", as this is too ambiguous. As a result, codecs were removed from the container list. - Added FFmpeg speed presets, so the user can choosen from the range "Very slow" to "Ultra fast". By default no preset is used. - Added Constant Rate Factor (CRF) mode, which allows changing the bit-rate depending on the desired quality and the input. This generally produces the best quality videos, at the expense of not knowing the exact bit-rate and file size. - Added optional maximum of non-B-frames between B-frames (`max_b_frames`). - Presets were adjusted for these changes, and new presets added. One of the new presets is [recommended](https://trac.ffmpeg.org/wiki/Encode/VFX#H.264) for reviewing videos, as it allows players to scrub through it easily. Might be nice in weeklies. This preset also requires control over the `max_b_frames` setting. GUI-only changes: - Renamed "MPEG" in the output file format menu with "FFmpeg", as this is more accurate. After all, FFmpeg is used when this option is chosen, which can also output non-MPEG files. - Certain parts of the GUI are disabled when not in use: - bit rate options are not used when a constant rate factor is given. - audio bitrate & volume are not used when no audio is exported. Note that I did not touch `BKE_ffmpeg_preset_set()`. There are currently two preset systems for FFmpeg (`BKE_ffmpeg_preset_set()` and the Python preset system). Before we do more work on `BKE_ffmpeg_preset_set()`, I think it's a good idea to determine whether we want to keep it at all. After this patch has been accepted, I'd be happy to go through the code and remove any then-obsolete bits, such as the handling of "XVID" as a container format. Reviewers: sergey, mont29, brecht Subscribers: mpan3, Blendify, brecht, fsiddi Tags: #bf_blender Differential Revision: https://developer.blender.org/D2242
-rw-r--r--release/scripts/presets/ffmpeg/DV.py14
-rw-r--r--release/scripts/presets/ffmpeg/DVD (note_colon_ this changes render resolution).py (renamed from release/scripts/presets/ffmpeg/DVD.py)0
-rw-r--r--release/scripts/presets/ffmpeg/SVCD.py24
-rw-r--r--release/scripts/presets/ffmpeg/VCD.py24
-rw-r--r--release/scripts/presets/ffmpeg/h264 in MP4.py18
-rw-r--r--release/scripts/presets/ffmpeg/h264 in Matroska for scrubbing.py14
-rw-r--r--release/scripts/presets/ffmpeg/h264 in Matroska.py (renamed from release/scripts/presets/ffmpeg/h264.py)2
-rw-r--r--release/scripts/presets/ffmpeg/ogg_theora.py (renamed from release/scripts/presets/ffmpeg/theora.py)1
-rw-r--r--release/scripts/presets/ffmpeg/xvid.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py40
-rw-r--r--source/blender/blenkernel/intern/image.c2
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c76
-rw-r--r--source/blender/blenloader/intern/versioning_270.c14
-rw-r--r--source/blender/makesdna/DNA_scene_types.h37
-rw-r--r--source/blender/makesrna/intern/rna_scene.c81
15 files changed, 240 insertions, 111 deletions
diff --git a/release/scripts/presets/ffmpeg/DV.py b/release/scripts/presets/ffmpeg/DV.py
deleted file mode 100644
index a95d861111a..00000000000
--- a/release/scripts/presets/ffmpeg/DV.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import bpy
-is_ntsc = (bpy.context.scene.render.fps != 25)
-
-bpy.context.scene.render.ffmpeg.format = "DV"
-bpy.context.scene.render.resolution_x = 720
-
-if is_ntsc:
- bpy.context.scene.render.resolution_y = 480
-else:
- bpy.context.scene.render.resolution_y = 576
-
-bpy.context.scene.render.ffmpeg.audio_mixrate = 48000
-bpy.context.scene.render.ffmpeg.audio_codec = "PCM"
-bpy.context.scene.render.ffmpeg.audio_channels = "STEREO"
diff --git a/release/scripts/presets/ffmpeg/DVD.py b/release/scripts/presets/ffmpeg/DVD (note_colon_ this changes render resolution).py
index d858bd70836..d858bd70836 100644
--- a/release/scripts/presets/ffmpeg/DVD.py
+++ b/release/scripts/presets/ffmpeg/DVD (note_colon_ this changes render resolution).py
diff --git a/release/scripts/presets/ffmpeg/SVCD.py b/release/scripts/presets/ffmpeg/SVCD.py
deleted file mode 100644
index deaf9697045..00000000000
--- a/release/scripts/presets/ffmpeg/SVCD.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import bpy
-is_ntsc = (bpy.context.scene.render.fps != 25)
-
-bpy.context.scene.render.ffmpeg.format = "MPEG2"
-bpy.context.scene.render.resolution_x = 480
-
-if is_ntsc:
- bpy.context.scene.render.resolution_y = 480
- bpy.context.scene.render.ffmpeg.gopsize = 18
-else:
- bpy.context.scene.render.resolution_y = 576
- bpy.context.scene.render.ffmpeg.gopsize = 15
-
-bpy.context.scene.render.ffmpeg.video_bitrate = 2040
-bpy.context.scene.render.ffmpeg.maxrate = 2516
-bpy.context.scene.render.ffmpeg.minrate = 0
-bpy.context.scene.render.ffmpeg.buffersize = 224 * 8
-bpy.context.scene.render.ffmpeg.packetsize = 2324
-bpy.context.scene.render.ffmpeg.muxrate = 0
-
-bpy.context.scene.render.ffmpeg.audio_bitrate = 224
-bpy.context.scene.render.ffmpeg.audio_mixrate = 44100
-bpy.context.scene.render.ffmpeg.audio_codec = "MP2"
-bpy.context.scene.render.ffmpeg.audio_channels = "STEREO"
diff --git a/release/scripts/presets/ffmpeg/VCD.py b/release/scripts/presets/ffmpeg/VCD.py
deleted file mode 100644
index 3e57be720fb..00000000000
--- a/release/scripts/presets/ffmpeg/VCD.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import bpy
-is_ntsc = (bpy.context.scene.render.fps != 25)
-
-bpy.context.scene.render.ffmpeg.format = "MPEG1"
-bpy.context.scene.render.resolution_x = 352
-
-if is_ntsc:
- bpy.context.scene.render.resolution_y = 240
- bpy.context.scene.render.ffmpeg.gopsize = 18
-else:
- bpy.context.scene.render.resolution_y = 288
- bpy.context.scene.render.ffmpeg.gopsize = 15
-
-bpy.context.scene.render.ffmpeg.video_bitrate = 1150
-bpy.context.scene.render.ffmpeg.maxrate = 1150
-bpy.context.scene.render.ffmpeg.minrate = 1150
-bpy.context.scene.render.ffmpeg.buffersize = 40 * 8
-bpy.context.scene.render.ffmpeg.packetsize = 2324
-bpy.context.scene.render.ffmpeg.muxrate = 2352 * 75 * 8
-
-bpy.context.scene.render.ffmpeg.audio_bitrate = 224
-bpy.context.scene.render.ffmpeg.audio_mixrate = 44100
-bpy.context.scene.render.ffmpeg.audio_codec = "MP2"
-bpy.context.scene.render.ffmpeg.audio_channels = "STEREO"
diff --git a/release/scripts/presets/ffmpeg/h264 in MP4.py b/release/scripts/presets/ffmpeg/h264 in MP4.py
new file mode 100644
index 00000000000..0e9c32c4878
--- /dev/null
+++ b/release/scripts/presets/ffmpeg/h264 in MP4.py
@@ -0,0 +1,18 @@
+import bpy
+is_ntsc = (bpy.context.scene.render.fps != 25)
+
+bpy.context.scene.render.ffmpeg.format = "MPEG4"
+bpy.context.scene.render.ffmpeg.codec = "H264"
+
+if is_ntsc:
+ bpy.context.scene.render.ffmpeg.gopsize = 18
+else:
+ bpy.context.scene.render.ffmpeg.gopsize = 15
+bpy.context.scene.render.ffmpeg.use_max_b_frames = False
+
+bpy.context.scene.render.ffmpeg.video_bitrate = 6000
+bpy.context.scene.render.ffmpeg.maxrate = 9000
+bpy.context.scene.render.ffmpeg.minrate = 0
+bpy.context.scene.render.ffmpeg.buffersize = 224 * 8
+bpy.context.scene.render.ffmpeg.packetsize = 2048
+bpy.context.scene.render.ffmpeg.muxrate = 10080000
diff --git a/release/scripts/presets/ffmpeg/h264 in Matroska for scrubbing.py b/release/scripts/presets/ffmpeg/h264 in Matroska for scrubbing.py
new file mode 100644
index 00000000000..eb1889d272f
--- /dev/null
+++ b/release/scripts/presets/ffmpeg/h264 in Matroska for scrubbing.py
@@ -0,0 +1,14 @@
+"""Sets up FFmpeg to output files that can easily be scrubbed through.
+
+Information was taken from https://trac.ffmpeg.org/wiki/Encode/VFX#H.264
+"""
+
+import bpy
+
+bpy.context.scene.render.ffmpeg.format = "MKV"
+bpy.context.scene.render.ffmpeg.codec = "H264"
+
+bpy.context.scene.render.ffmpeg.gopsize = 1
+bpy.context.scene.render.ffmpeg.constant_rate_factor = 'PERC_LOSSLESS'
+bpy.context.scene.render.ffmpeg.use_max_b_frames = True
+bpy.context.scene.render.ffmpeg.max_b_frames = 0
diff --git a/release/scripts/presets/ffmpeg/h264.py b/release/scripts/presets/ffmpeg/h264 in Matroska.py
index e1dbdc1d8b9..1fe066dc4bf 100644
--- a/release/scripts/presets/ffmpeg/h264.py
+++ b/release/scripts/presets/ffmpeg/h264 in Matroska.py
@@ -1,7 +1,7 @@
import bpy
is_ntsc = (bpy.context.scene.render.fps != 25)
-bpy.context.scene.render.ffmpeg.format = "H264"
+bpy.context.scene.render.ffmpeg.format = "MKV"
bpy.context.scene.render.ffmpeg.codec = "H264"
if is_ntsc:
diff --git a/release/scripts/presets/ffmpeg/theora.py b/release/scripts/presets/ffmpeg/ogg_theora.py
index 88f1ac9bbae..b450b67fd98 100644
--- a/release/scripts/presets/ffmpeg/theora.py
+++ b/release/scripts/presets/ffmpeg/ogg_theora.py
@@ -8,6 +8,7 @@ if is_ntsc:
bpy.context.scene.render.ffmpeg.gopsize = 18
else:
bpy.context.scene.render.ffmpeg.gopsize = 15
+bpy.context.scene.render.ffmpeg.use_max_b_frames = False
bpy.context.scene.render.ffmpeg.video_bitrate = 6000
bpy.context.scene.render.ffmpeg.maxrate = 9000
diff --git a/release/scripts/presets/ffmpeg/xvid.py b/release/scripts/presets/ffmpeg/xvid.py
index e69ab663dc8..dba0f71c279 100644
--- a/release/scripts/presets/ffmpeg/xvid.py
+++ b/release/scripts/presets/ffmpeg/xvid.py
@@ -1,12 +1,14 @@
import bpy
is_ntsc = (bpy.context.scene.render.fps != 25)
-bpy.context.scene.render.ffmpeg.format = "XVID"
+bpy.context.scene.render.ffmpeg.format = "AVI"
+bpy.context.scene.render.ffmpeg.codec = "MPEG4"
if is_ntsc:
bpy.context.scene.render.ffmpeg.gopsize = 18
else:
bpy.context.scene.render.ffmpeg.gopsize = 15
+bpy.context.scene.render.ffmpeg.use_max_b_frames = False
bpy.context.scene.render.ffmpeg.video_bitrate = 6000
bpy.context.scene.render.ffmpeg.maxrate = 9000
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index 152d4e96d5d..850606eb80b 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -461,31 +461,42 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel):
split = layout.split()
split.prop(rd.ffmpeg, "format")
- if ffmpeg.format in {'AVI', 'QUICKTIME', 'MKV', 'OGG', 'MPEG4'}:
- split.prop(ffmpeg, "codec")
- if ffmpeg.codec == 'H264':
- row = layout.row()
- row.label()
- row.prop(ffmpeg, "use_lossless_output")
- elif rd.ffmpeg.format == 'H264':
- split.prop(ffmpeg, "use_lossless_output")
- else:
- split.label()
+ split.prop(ffmpeg, "use_autosplit")
+
+ layout.separator()
+
+ needs_codec = ffmpeg.format in {'AVI', 'QUICKTIME', 'MKV', 'OGG', 'MPEG4'}
+ if needs_codec:
+ layout.prop(ffmpeg, "codec")
+ if ffmpeg.codec in {'DNXHD'}:
+ layout.prop(ffmpeg, "use_lossless_output")
+
+ # Output quality
+ if needs_codec and ffmpeg.codec in {'H264', 'MPEG4'}:
+ layout.prop(ffmpeg, "constant_rate_factor")
+
+ # Encoding speed
+ layout.prop(ffmpeg, "ffmpeg_preset")
+ # I-frames
+ layout.prop(ffmpeg, "gopsize")
+ # B-Frames
row = layout.row()
- row.prop(ffmpeg, "video_bitrate")
- row.prop(ffmpeg, "gopsize")
+ row.prop(ffmpeg, "use_max_b_frames", text='Max B-frames')
+ pbox = row.split()
+ pbox.prop(ffmpeg, "max_b_frames", text='')
+ pbox.enabled = ffmpeg.use_max_b_frames
split = layout.split()
-
+ split.enabled = ffmpeg.constant_rate_factor == 'NONE'
col = split.column()
col.label(text="Rate:")
+ col.prop(ffmpeg, "video_bitrate")
col.prop(ffmpeg, "minrate", text="Minimum")
col.prop(ffmpeg, "maxrate", text="Maximum")
col.prop(ffmpeg, "buffersize", text="Buffer")
col = split.column()
- col.prop(ffmpeg, "use_autosplit")
col.label(text="Mux:")
col.prop(ffmpeg, "muxrate", text="Rate")
col.prop(ffmpeg, "packetsize", text="Packet Size")
@@ -497,6 +508,7 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel):
layout.prop(ffmpeg, "audio_codec", text="Audio Codec")
row = layout.row()
+ row.enabled = ffmpeg.audio_codec != 'NONE'
row.prop(ffmpeg, "audio_bitrate")
row.prop(ffmpeg, "audio_volume", slider=True)
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 8a9cb73c8c9..a4eef2f9230 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -1325,7 +1325,7 @@ char BKE_imtype_from_arg(const char *imtype_arg)
else if (STREQ(imtype_arg, "EXR")) return R_IMF_IMTYPE_OPENEXR;
else if (STREQ(imtype_arg, "MULTILAYER")) return R_IMF_IMTYPE_MULTILAYER;
#endif
- else if (STREQ(imtype_arg, "MPEG")) return R_IMF_IMTYPE_FFMPEG;
+ else if (STREQ(imtype_arg, "FFMPEG")) return R_IMF_IMTYPE_FFMPEG;
else if (STREQ(imtype_arg, "FRAMESERVER")) return R_IMF_IMTYPE_FRAMESERVER;
#ifdef WITH_CINEON
else if (STREQ(imtype_arg, "CINEON")) return R_IMF_IMTYPE_CINEON;
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index de55a1977bf..b0ab6f707fa 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -69,10 +69,14 @@ typedef struct FFMpegContext {
int ffmpeg_video_bitrate;
int ffmpeg_audio_bitrate;
int ffmpeg_gop_size;
+ int ffmpeg_max_b_frames;
int ffmpeg_autosplit;
int ffmpeg_autosplit_count;
bool ffmpeg_preview;
+ int ffmpeg_crf; /* set to 0 to not use CRF mode; we have another flag for lossless anyway. */
+ int ffmpeg_preset; /* see FFMpegPreset */
+
AVFormatContext *outfile;
AVStream *video_stream;
AVStream *audio_stream;
@@ -560,10 +564,37 @@ static AVStream *alloc_video_stream(FFMpegContext *context, RenderData *rd, int
}
c->gop_size = context->ffmpeg_gop_size;
- c->bit_rate = context->ffmpeg_video_bitrate * 1000;
- c->rc_max_rate = rd->ffcodecdata.rc_max_rate * 1000;
- c->rc_min_rate = rd->ffcodecdata.rc_min_rate * 1000;
- c->rc_buffer_size = rd->ffcodecdata.rc_buffer_size * 1024;
+ c->max_b_frames = context->ffmpeg_max_b_frames;
+
+ if (context->ffmpeg_crf >= 0) {
+ ffmpeg_dict_set_int(&opts, "crf", context->ffmpeg_crf);
+ } else {
+ c->bit_rate = context->ffmpeg_video_bitrate * 1000;
+ c->rc_max_rate = rd->ffcodecdata.rc_max_rate * 1000;
+ c->rc_min_rate = rd->ffcodecdata.rc_min_rate * 1000;
+ c->rc_buffer_size = rd->ffcodecdata.rc_buffer_size * 1024;
+ }
+
+ if (context->ffmpeg_preset) {
+ char const * preset_name;
+ 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;
+ 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 0
/* this options are not set in ffmpeg.c and leads to artifacts with MPEG-4
@@ -819,6 +850,12 @@ static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int
context->ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate;
context->ffmpeg_gop_size = rd->ffcodecdata.gop_size;
context->ffmpeg_autosplit = rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT;
+ context->ffmpeg_crf = rd->ffcodecdata.constant_rate_factor;
+ context->ffmpeg_preset = rd->ffcodecdata.ffmpeg_preset;
+
+ if ((rd->ffcodecdata.flags & FFMPEG_USE_MAX_B_FRAMES) != 0) {
+ context->ffmpeg_max_b_frames = rd->ffcodecdata.max_b_frames;
+ }
/* Determine the correct filename */
ffmpeg_filepath_get(context, name, rd, context->ffmpeg_preview, suffix);
@@ -852,12 +889,16 @@ static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int
/* Returns after this must 'goto fail;' */
of->oformat = fmt;
- of->packet_size = rd->ffcodecdata.mux_packet_size;
- if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) {
- ffmpeg_dict_set_int(&opts, "muxrate", rd->ffcodecdata.mux_rate);
- }
- else {
- av_dict_set(&opts, "muxrate", "0", 0);
+
+ /* Only bother with setting packet size & mux rate when CRF is not used. */
+ if (context->ffmpeg_crf == 0) {
+ of->packet_size = rd->ffcodecdata.mux_packet_size;
+ if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) {
+ ffmpeg_dict_set_int(&opts, "muxrate", rd->ffcodecdata.mux_rate);
+ }
+ else {
+ av_dict_set(&opts, "muxrate", "0", 0);
+ }
}
ffmpeg_dict_set_int(&opts, "preload", (int)(0.5 * AV_TIME_BASE));
@@ -1503,14 +1544,6 @@ static void ffmpeg_set_expert_options(RenderData *rd)
BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1");
BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2");
#endif
-
- if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) {
-#ifdef FFMPEG_HAVE_DEPRECATED_FLAGS2
- BKE_ffmpeg_property_add_string(rd, "video", "cqp:0");
-#else
- BKE_ffmpeg_property_add_string(rd, "video", "qp:0");
-#endif
- }
}
else if (codec_id == AV_CODEC_ID_DNXHD) {
if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT)
@@ -1622,9 +1655,10 @@ void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf)
rd->ffcodecdata.audio_codec <= 0 ||
rd->ffcodecdata.video_bitrate <= 1)
{
- rd->ffcodecdata.codec = AV_CODEC_ID_MPEG2VIDEO;
-
- BKE_ffmpeg_preset_set(rd, FFMPEG_PRESET_DVD);
+ 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.type = FFMPEG_MKV;
}
if (rd->ffcodecdata.type == FFMPEG_OGG) {
rd->ffcodecdata.type = FFMPEG_MPEG2;
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index dfaa59c4e3f..aa5ef1c98cf 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -1392,4 +1392,18 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
}
+ if (!MAIN_VERSION_ATLEAST(main, 279, 0)) {
+ if (!DNA_struct_elem_find(fd->filesdna, "FFMpegCodecData", "int", "ffmpeg_preset")) {
+ for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
+ /* "medium" is the preset FFmpeg uses when no presets are given. */
+ scene->r.ffcodecdata.ffmpeg_preset = FFM_PRESET_MEDIUM;
+ }
+ }
+ if (!DNA_struct_elem_find(fd->filesdna, "FFMpegCodecData", "int", "constant_rate_factor")) {
+ for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
+ /* fall back to behaviour from before we introduced CRF for old files */
+ scene->r.ffcodecdata.constant_rate_factor = FFM_CRF_NONE;
+ }
+ }
+ }
}
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 5c5264afcba..94f23197293 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -135,6 +135,37 @@ typedef struct QuicktimeCodecSettings {
int pad1;
} QuicktimeCodecSettings;
+typedef enum FFMpegPreset {
+ 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,
+} FFMpegPreset;
+
+
+/* Mapping from easily-understandable descriptions to CRF values.
+ * Assumes we output 8-bit video. Needs to be remapped if 10-bit
+ * is output.
+ * We use a slightly wider than "subjectively sane range" according
+ * to https://trac.ffmpeg.org/wiki/Encode/H.264#a1.ChooseaCRFvalue
+ */
+typedef enum FFMpegCrf {
+ FFM_CRF_NONE = -1,
+ FFM_CRF_LOSSLESS = 0,
+ FFM_CRF_PERC_LOSSLESS = 17,
+ FFM_CRF_HIGH = 20,
+ FFM_CRF_MEDIUM = 23,
+ FFM_CRF_LOW = 26,
+ FFM_CRF_VERYLOW = 29,
+ FFM_CRF_LOWEST = 32,
+} FFMpegCrf;
+
typedef struct FFMpegCodecData {
int type;
int codec;
@@ -146,13 +177,18 @@ typedef struct FFMpegCodecData {
int audio_pad;
float audio_volume;
int gop_size;
+ int max_b_frames; /* only used if FFMPEG_USE_MAX_B_FRAMES flag is set. */
int flags;
+ int constant_rate_factor;
+ int ffmpeg_preset; /* see FFMpegPreset */
int rc_min_rate;
int rc_max_rate;
int rc_buffer_size;
int mux_packet_size;
int mux_rate;
+ int pad1;
+
IDProperty *properties;
} FFMpegCodecData;
@@ -1982,6 +2018,7 @@ enum {
#endif
FFMPEG_AUTOSPLIT_OUTPUT = 2,
FFMPEG_LOSSLESS_OUTPUT = 4,
+ FFMPEG_USE_MAX_B_FRAMES = (1 << 3),
};
/* Paint.flags */
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index bd6674f899c..c63d4e775f8 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -282,16 +282,11 @@ EnumPropertyItem rna_enum_image_type_items[] = {
{R_IMF_IMTYPE_FRAMESERVER, "FRAMESERVER", ICON_FILE_SCRIPT, "Frame Server", "Output image to a frameserver"},
#endif
#ifdef WITH_FFMPEG
- {R_IMF_IMTYPE_H264, "H264", ICON_FILE_MOVIE, "H.264", "Output video in H.264 format"},
- {R_IMF_IMTYPE_FFMPEG, "FFMPEG", ICON_FILE_MOVIE, "MPEG", "Output video in MPEG format"},
- {R_IMF_IMTYPE_THEORA, "THEORA", ICON_FILE_MOVIE, "Ogg Theora", "Output video in Ogg format"},
+ {R_IMF_IMTYPE_FFMPEG, "FFMPEG", ICON_FILE_MOVIE, "FFmpeg video", "The most versatile way to output video files"},
#endif
#ifdef WITH_QUICKTIME
{R_IMF_IMTYPE_QUICKTIME, "QUICKTIME", ICON_FILE_MOVIE, "QuickTime", "Output video in Quicktime format"},
#endif
-#ifdef WITH_FFMPEG
- {R_IMF_IMTYPE_XVID, "XVID", ICON_FILE_MOVIE, "Xvid", "Output video in Xvid format"},
-#endif
{0, NULL, 0, NULL, NULL}
};
@@ -5320,6 +5315,7 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
PropertyRNA *prop;
#ifdef WITH_FFMPEG
+ /* Container types */
static EnumPropertyItem ffmpeg_format_items[] = {
{FFMPEG_MPEG1, "MPEG1", 0, "MPEG-1", ""},
{FFMPEG_MPEG2, "MPEG2", 0, "MPEG-2", ""},
@@ -5327,8 +5323,8 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
{FFMPEG_AVI, "AVI", 0, "AVI", ""},
{FFMPEG_MOV, "QUICKTIME", 0, "Quicktime", ""},
{FFMPEG_DV, "DV", 0, "DV", ""},
- {FFMPEG_H264, "H264", 0, "H.264", ""},
- {FFMPEG_XVID, "XVID", 0, "Xvid", ""},
+// {FFMPEG_H264, "H264", 0, "H.264", ""}, not a container
+// {FFMPEG_XVID, "XVID", 0, "Xvid", ""}, not a container
{FFMPEG_OGG, "OGG", 0, "Ogg", ""},
{FFMPEG_MKV, "MKV", 0, "Matroska", ""},
{FFMPEG_FLV, "FLASH", 0, "Flash", ""},
@@ -5352,6 +5348,32 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
+ static 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", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem ffmpeg_crf_items[] = {
+ {FFM_CRF_NONE, "NONE", 0, "None; use constant bit-rate",
+ "Use constant bit rate, rather than constant output quality"},
+ {FFM_CRF_LOSSLESS, "LOSSLESS", 0, "Lossless", ""},
+ {FFM_CRF_PERC_LOSSLESS, "PERC_LOSSLESS", 0, "Perceptually lossless", ""},
+ {FFM_CRF_HIGH, "HIGH", 0, "High quality", ""},
+ {FFM_CRF_MEDIUM, "MEDIUM", 0, "Medium quality", ""},
+ {FFM_CRF_LOW, "LOW", 0, "Low quality", ""},
+ {FFM_CRF_VERYLOW, "VERYLOW", 0, "Very low quality", ""},
+ {FFM_CRF_LOWEST, "LOWEST", 0, "Lowest quality", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
static EnumPropertyItem ffmpeg_audio_codec_items[] = {
{AV_CODEC_ID_NONE, "NONE", 0, "None", ""},
{AV_CODEC_ID_MP2, "MP2", 0, "MP2", ""},
@@ -5383,13 +5405,15 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
RNA_def_property_enum_bitflag_sdna(prop, NULL, "type");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_enum_items(prop, ffmpeg_format_items);
- RNA_def_property_ui_text(prop, "Format", "Output file format");
+ RNA_def_property_enum_default(prop, FFMPEG_MKV);
+ RNA_def_property_ui_text(prop, "Container", "Output file container");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_settings_update");
prop = RNA_def_property(srna, "codec", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "codec");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_enum_items(prop, ffmpeg_codec_items);
+ RNA_def_property_enum_default(prop, AV_CODEC_ID_H264);
RNA_def_property_ui_text(prop, "Codec", "FFmpeg codec to use");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_settings_update");
@@ -5421,8 +5445,25 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "gopsize", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "gop_size");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_range(prop, 0, 100);
- RNA_def_property_ui_text(prop, "GOP Size", "Distance between key frames");
+ RNA_def_property_range(prop, 0, 500);
+ RNA_def_property_int_default(prop, 25);
+ RNA_def_property_ui_text(prop, "Keyframe interval",
+ "Distance between key frames, also known as GOP size; "
+ "influences file size and seekability");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "max_b_frames", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "max_b_frames");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_range(prop, 0, 16);
+ RNA_def_property_ui_text(prop, "Max B-frames",
+ "Maximum number of B-frames between non-B-frames; influences file size and seekability");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "use_max_b_frames", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", FFMPEG_USE_MAX_B_FRAMES);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Use max B-frames", "Set a maximum number of B-frames");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "buffersize", PROP_INT, PROP_NONE);
@@ -5439,6 +5480,24 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Mux Packet Size", "Mux packet size (byte)");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+ prop = RNA_def_property(srna, "constant_rate_factor", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "constant_rate_factor");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, ffmpeg_crf_items);
+ RNA_def_property_enum_default(prop, FFM_CRF_MEDIUM);
+ RNA_def_property_ui_text(prop, "Output quality",
+ "Constant Rate Factor (CRF); tradeoff between video quality and file size");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "ffmpeg_preset", PROP_ENUM, PROP_NONE);
+ 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_ui_text(prop, "Encoding speed",
+ "Tradeoff between encoding speed and compression ratio");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
prop = RNA_def_property(srna, "use_autosplit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", FFMPEG_AUTOSPLIT_OUTPUT);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);