Skip to content

Commit 1bf3e37

Browse files
authored
Refactor FFmpegWriter Open() and PrepareStreams() methods, so that SetOption() is can be called between them, and allowed to adjust the codecs bit_rate and options before we call open_video(). This was primarily to allow CRF options to work. (#193)
1 parent eeeec31 commit 1bf3e37

File tree

1 file changed

+51
-31
lines changed

1 file changed

+51
-31
lines changed

src/FFmpegWriter.cpp

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,24 @@ FFmpegWriter::FFmpegWriter(string path) :
5555
// Open the writer
5656
void FFmpegWriter::Open()
5757
{
58-
// Open the writer
59-
is_open = true;
60-
61-
// Prepare streams (if needed)
62-
if (!prepare_streams)
63-
PrepareStreams();
64-
65-
// Write header (if needed)
66-
if (!write_header)
67-
WriteHeader();
58+
if (!is_open) {
59+
// Open the writer
60+
is_open = true;
61+
62+
// Prepare streams (if needed)
63+
if (!prepare_streams)
64+
PrepareStreams();
65+
66+
// Now that all the parameters are set, we can open the audio and video codecs and allocate the necessary encode buffers
67+
if (info.has_video && video_st)
68+
open_video(oc, video_st);
69+
if (info.has_audio && audio_st)
70+
open_audio(oc, audio_st);
71+
72+
// Write header (if needed)
73+
if (!write_header)
74+
WriteHeader();
75+
}
6876
}
6977

7078
// auto detect format (from path)
@@ -148,8 +156,8 @@ void FFmpegWriter::SetVideoOptions(bool has_video, string codec, Fraction fps, i
148156
}
149157
if (bit_rate >= 1000) // bit_rate is the bitrate in b/s
150158
info.video_bit_rate = bit_rate;
151-
else
152-
info.video_bit_rate = 0;
159+
if ((bit_rate >= 0) && (bit_rate < 64) ) // bit_rate is the bitrate in crf
160+
info.video_bit_rate = bit_rate;
153161

154162
info.interlaced_frame = interlaced;
155163
info.top_field_first = top_field_first;
@@ -293,29 +301,50 @@ void FFmpegWriter::SetOption(StreamType stream, string name, string value)
293301
// and way to set quality are possible
294302
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101)
295303
switch (c->codec_id) {
296-
case AV_CODEC_ID_VP8 :
304+
#if (LIBAVCODEC_VERSION_MAJOR >= 58)
305+
case AV_CODEC_ID_AV1 :
306+
c->bit_rate = 0;
297307
av_opt_set_int(c->priv_data, "crf", min(stoi(value),63), 0);
298308
break;
309+
#endif
310+
case AV_CODEC_ID_VP8 :
311+
c->bit_rate = 10000000;
312+
av_opt_set_int(c->priv_data, "crf", max(min(stoi(value),63),4), 0); // 4-63
313+
break;
299314
case AV_CODEC_ID_VP9 :
300-
av_opt_set_int(c->priv_data, "crf", min(stoi(value),63), 0);
315+
c->bit_rate = 0; // Must be zero!
316+
av_opt_set_int(c->priv_data, "crf", min(stoi(value),63), 0); // 0-63
301317
if (stoi(value) == 0) {
318+
av_opt_set(c->priv_data, "preset", "veryslow", 0);
302319
av_opt_set_int(c->priv_data, "lossless", 1, 0);
303320
}
304321
break;
305322
case AV_CODEC_ID_H264 :
306-
av_opt_set_int(c->priv_data, "crf", min(stoi(value),51), 0);
323+
av_opt_set_int(c->priv_data, "crf", min(stoi(value),51), 0); // 0-51
324+
if (stoi(value) == 0) {
325+
av_opt_set(c->priv_data, "preset", "veryslow", 0);
326+
}
307327
break;
308328
case AV_CODEC_ID_H265 :
309-
av_opt_set_int(c->priv_data, "crf", min(stoi(value),51), 0);
329+
av_opt_set_int(c->priv_data, "crf", min(stoi(value),51), 0); // 0-51
310330
if (stoi(value) == 0) {
331+
av_opt_set(c->priv_data, "preset", "veryslow", 0);
311332
av_opt_set_int(c->priv_data, "lossless", 1, 0);
312-
}
313-
break;
314-
#ifdef AV_CODEC_ID_AV1
315-
case AV_CODEC_ID_AV1 :
316-
av_opt_set_int(c->priv_data, "crf", min(stoi(value),63), 0);
333+
}
317334
break;
318-
#endif
335+
default:
336+
// If this codec doesn't support crf calculate a bitrate
337+
// TODO: find better formula
338+
double mbs = 15000000.0;
339+
if (info.video_bit_rate > 0) {
340+
if (info.video_bit_rate > 42) {
341+
mbs = 380.0;
342+
}
343+
else {
344+
mbs *= pow(0.912,info.video_bit_rate);
345+
}
346+
}
347+
c->bit_rate = (int)(mbs);
319348
}
320349
#endif
321350
}
@@ -355,12 +384,6 @@ void FFmpegWriter::PrepareStreams()
355384
// Initialize the streams (i.e. add the streams)
356385
initialize_streams();
357386

358-
// Now that all the parameters are set, we can open the audio and video codecs and allocate the necessary encode buffers
359-
if (info.has_video && video_st)
360-
open_video(oc, video_st);
361-
if (info.has_audio && audio_st)
362-
open_audio(oc, audio_st);
363-
364387
// Mark as 'prepared'
365388
prepare_streams = true;
366389
}
@@ -973,9 +996,6 @@ AVStream* FFmpegWriter::add_video_stream()
973996
if (info.video_bit_rate >= 1000) {
974997
c->bit_rate = info.video_bit_rate;
975998
}
976-
else {
977-
c->bit_rate = 0;
978-
}
979999

9801000
//TODO: Implement variable bitrate feature (which actually works). This implementation throws
9811001
//invalid bitrate errors and rc buffer underflow errors, etc...

0 commit comments

Comments
 (0)