summaryrefslogtreecommitdiffstats
path: root/mpvcore/encode_lavc.c
diff options
context:
space:
mode:
authorStefano Pigozzi <stefano.pigozzi@gmail.com>2013-08-06 22:34:12 +0200
committerStefano Pigozzi <stefano.pigozzi@gmail.com>2013-08-06 22:48:47 +0200
commitbc27f946c27e33933a3a696cade78a04902c8dce (patch)
tree0e4166ba66072d407d0b972842b08cb7fea6f50b /mpvcore/encode_lavc.c
parentd40a91e804e19fb32430c5a80a984f5148324f52 (diff)
downloadmpv-bc27f946c27e33933a3a696cade78a04902c8dce.tar.bz2
mpv-bc27f946c27e33933a3a696cade78a04902c8dce.tar.xz
core: move contents to mpvcore (1/2)
core is used in many unix systems for core dumps. For that reason some tools work under the assumption that the file is indeed a core dump (for example autoconf does this). This commit just renames the files. The following one will change all the includes to fix compilation. This is done this way because git has a easier time tracing file changes if there is a pure rename commit.
Diffstat (limited to 'mpvcore/encode_lavc.c')
-rw-r--r--mpvcore/encode_lavc.c1115
1 files changed, 1115 insertions, 0 deletions
diff --git a/mpvcore/encode_lavc.c b/mpvcore/encode_lavc.c
new file mode 100644
index 0000000000..75e57a2443
--- /dev/null
+++ b/mpvcore/encode_lavc.c
@@ -0,0 +1,1115 @@
+/*
+ * muxing using libavformat
+ * Copyright (C) 2010 Nicolas George <george@nsup.org>
+ * Copyright (C) 2011-2012 Rudolf Polzer <divVerent@xonotic.org>
+ *
+ * This file is part of mpv.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "encode_lavc.h"
+#include "core/mp_msg.h"
+#include "video/vfcap.h"
+#include "core/options.h"
+#include "osdep/timer.h"
+#include "video/out/vo.h"
+#include "talloc.h"
+#include "stream/stream.h"
+
+static int set_to_avdictionary(AVDictionary **dictp, const char *key,
+ const char *val)
+{
+ char keybuf[1024];
+ char valuebuf[1024];
+
+ if (key == NULL) {
+ // we need to split at equals sign
+ const char *equals = strchr(val, '=');
+ if (!equals || equals - val >= sizeof(keybuf)) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "encode-lavc: option '%s' does not contain an equals sign\n",
+ val);
+ return 0;
+ }
+ memcpy(keybuf, val, equals - val);
+ keybuf[equals - val] = 0;
+ key = keybuf;
+ val = equals + 1;
+ }
+
+ // hack: support "qscale" key as virtual "global_quality" key that multiplies by QP2LAMBDA
+ if (!strcmp(key, "qscale")) {
+ key = "global_quality";
+ snprintf(valuebuf, sizeof(valuebuf),
+ "%.1s(%s)*QP2LAMBDA",
+ (val[0] == '+' || val[0] == '-') ? val : "",
+ (val[0] == '+' || val[0] == '-') ? val + 1 : val);
+ valuebuf[sizeof(valuebuf) - 1] = 0;
+ val = valuebuf;
+ }
+
+ mp_msg(MSGT_ENCODE, MSGL_V,
+ "encode-lavc: setting value '%s' for key '%s'\n",
+ val,
+ key);
+
+ if (av_dict_set(dictp, key, *val ? val : NULL,
+ (val[0] == '+' || val[0] == '-') ? AV_DICT_APPEND : 0) >= 0)
+ return 1;
+
+ return 0;
+}
+
+static bool value_has_flag(const char *value, const char *flag)
+{
+ bool state = true;
+ bool ret = false;
+ while (*value) {
+ size_t l = strcspn(value, "+-");
+ if (l == 0) {
+ state = (*value == '+');
+ ++value;
+ } else {
+ if (l == strlen(flag))
+ if (!memcmp(value, flag, l))
+ ret = state;
+ value += l;
+ }
+ }
+ return ret;
+}
+
+#define CHECK_FAIL(ctx, val) \
+ if (ctx && (ctx->failed || ctx->finished)) { \
+ mp_msg(MSGT_ENCODE, MSGL_ERR, \
+ "Called a function on a %s encoding context. Bailing out.\n", \
+ ctx->failed ? "failed" : "finished"); \
+ return val; \
+ }
+
+int encode_lavc_available(struct encode_lavc_context *ctx)
+{
+ CHECK_FAIL(ctx, 0);
+ return ctx && ctx->avc;
+}
+
+int encode_lavc_oformat_flags(struct encode_lavc_context *ctx)
+{
+ CHECK_FAIL(ctx, 0);
+ return ctx->avc ? ctx->avc->oformat->flags : 0;
+}
+
+struct encode_lavc_context *encode_lavc_init(struct encode_output_conf *options)
+{
+ struct encode_lavc_context *ctx;
+ const char *filename = options->file;
+
+ // STUPID STUPID STUPID STUPID avio
+ // does not support "-" as file name to mean stdin/stdout
+ // ffmpeg.c works around this too, the same way
+ if (!strcmp(filename, "-"))
+ filename = "pipe:1";
+
+ if (filename && (
+ !strcmp(filename, "/dev/stdout") ||
+ !strcmp(filename, "pipe:") ||
+ !strcmp(filename, "pipe:1")))
+ mp_msg_stdout_in_use = 1;
+
+ ctx = talloc_zero(NULL, struct encode_lavc_context);
+ encode_lavc_discontinuity(ctx);
+ ctx->options = options;
+
+ ctx->avc = avformat_alloc_context();
+
+ if (ctx->options->format) {
+ char *tok;
+ const char *in = ctx->options->format;
+ while (*in) {
+ tok = av_get_token(&in, ",");
+ ctx->avc->oformat = av_guess_format(tok, filename, NULL);
+ av_free(tok);
+ if (ctx->avc->oformat)
+ break;
+ if (*in)
+ ++in;
+ }
+ } else
+ ctx->avc->oformat = av_guess_format(NULL, filename, NULL);
+
+ if (!ctx->avc->oformat) {
+ encode_lavc_fail(ctx, "encode-lavc: format not found\n");
+ return NULL;
+ }
+
+ av_strlcpy(ctx->avc->filename, filename,
+ sizeof(ctx->avc->filename));
+
+ ctx->foptions = NULL;
+ if (ctx->options->fopts) {
+ char **p;
+ for (p = ctx->options->fopts; *p; ++p) {
+ if (!set_to_avdictionary(&ctx->foptions, NULL, *p))
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "encode-lavc: could not set option %s\n", *p);
+ }
+ }
+
+ if (ctx->options->vcodec) {
+ char *tok;
+ const char *in = ctx->options->vcodec;
+ while (*in) {
+ tok = av_get_token(&in, ",");
+ ctx->vc = avcodec_find_encoder_by_name(tok);
+ av_free(tok);
+ if (ctx->vc && ctx->vc->type != AVMEDIA_TYPE_VIDEO)
+ ctx->vc = NULL;
+ if (ctx->vc)
+ break;
+ if (*in)
+ ++in;
+ }
+ } else
+ ctx->vc = avcodec_find_encoder(av_guess_codec(ctx->avc->oformat, NULL,
+ ctx->avc->filename, NULL,
+ AVMEDIA_TYPE_VIDEO));
+
+ if (ctx->options->acodec) {
+ char *tok;
+ const char *in = ctx->options->acodec;
+ while (*in) {
+ tok = av_get_token(&in, ",");
+ ctx->ac = avcodec_find_encoder_by_name(tok);
+ av_free(tok);
+ if (ctx->ac && ctx->ac->type != AVMEDIA_TYPE_AUDIO)
+ ctx->ac = NULL;
+ if (ctx->ac)
+ break;
+ if (*in)
+ ++in;
+ }
+ } else
+ ctx->ac = avcodec_find_encoder(av_guess_codec(ctx->avc->oformat, NULL,
+ ctx->avc->filename, NULL,
+ AVMEDIA_TYPE_AUDIO));
+
+ if (!ctx->vc && !ctx->ac) {
+ encode_lavc_fail(
+ ctx, "encode-lavc: neither audio nor video codec was found\n");
+ return NULL;
+ }
+
+ /* taken from ffmpeg unchanged
+ * TODO turn this into an option if anyone needs this */
+
+ ctx->avc->max_delay = 0.7 * AV_TIME_BASE;
+
+ ctx->abytes = 0;
+ ctx->vbytes = 0;
+ ctx->frames = 0;
+
+ if (options->video_first)
+ ctx->video_first = true;
+ if (options->audio_first)
+ ctx->audio_first = true;
+
+ return ctx;
+}
+
+int encode_lavc_start(struct encode_lavc_context *ctx)
+{
+ AVDictionaryEntry *de;
+ unsigned i;
+
+ if (ctx->header_written < 0)
+ return 0;
+ if (ctx->header_written > 0)
+ return 1;
+
+ CHECK_FAIL(ctx, 0);
+
+ if (ctx->expect_video) {
+ for (i = 0; i < ctx->avc->nb_streams; ++i)
+ if (ctx->avc->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+ break;
+ if (i >= ctx->avc->nb_streams) {
+ encode_lavc_fail(ctx,
+ "encode-lavc: video stream missing, invalid codec?\n");
+ return 0;
+ }
+ }
+ if (ctx->expect_audio) {
+ for (i = 0; i < ctx->avc->nb_streams; ++i)
+ if (ctx->avc->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+ break;
+ if (i >= ctx->avc->nb_streams) {
+ encode_lavc_fail(ctx,
+ "encode-lavc: audio stream missing, invalid codec?\n");
+ return 0;
+ }
+ }
+
+ ctx->header_written = -1;
+
+ if (!(ctx->avc->oformat->flags & AVFMT_NOFILE)) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Opening output file: %s\n",
+ ctx->avc->filename);
+
+ if (avio_open(&ctx->avc->pb, ctx->avc->filename,
+ AVIO_FLAG_WRITE) < 0) {
+ encode_lavc_fail(ctx, "encode-lavc: could not open '%s'\n",
+ ctx->avc->filename);
+ return 0;
+ }
+ }
+
+ ctx->t0 = mp_time_sec();
+
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Opening muxer: %s [%s]\n",
+ ctx->avc->oformat->long_name, ctx->avc->oformat->name);
+
+ if (avformat_write_header(ctx->avc, &ctx->foptions) < 0) {
+ encode_lavc_fail(ctx, "encode-lavc: could not write header\n");
+ return 0;
+ }
+
+ for (de = NULL; (de = av_dict_get(ctx->foptions, "", de,
+ AV_DICT_IGNORE_SUFFIX));)
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "ofopts: key '%s' not found.\n", de->key);
+ av_dict_free(&ctx->foptions);
+
+ ctx->header_written = 1;
+ return 1;
+}
+
+void encode_lavc_free(struct encode_lavc_context *ctx)
+{
+ if (!ctx)
+ return;
+
+ if (!ctx->finished)
+ encode_lavc_fail(ctx,
+ "called encode_lavc_free without encode_lavc_finish\n");
+
+ talloc_free(ctx);
+}
+
+void encode_lavc_finish(struct encode_lavc_context *ctx)
+{
+ unsigned i;
+
+ if (!ctx)
+ return;
+
+ if (ctx->finished)
+ return;
+
+ if (ctx->avc) {
+ if (ctx->header_written > 0)
+ av_write_trailer(ctx->avc); // this is allowed to fail
+
+ for (i = 0; i < ctx->avc->nb_streams; i++) {
+ switch (ctx->avc->streams[i]->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (ctx->twopass_bytebuffer_v) {
+ char *stats = ctx->avc->streams[i]->codec->stats_out;
+ if (stats)
+ stream_write_buffer(ctx->twopass_bytebuffer_v,
+ stats, strlen(stats));
+ }
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (ctx->twopass_bytebuffer_a) {
+ char *stats = ctx->avc->streams[i]->codec->stats_out;
+ if (stats)
+ stream_write_buffer(ctx->twopass_bytebuffer_a,
+ stats, strlen(stats));
+ }
+ break;
+ default:
+ break;
+ }
+ avcodec_close(ctx->avc->streams[i]->codec);
+ talloc_free(ctx->avc->streams[i]->codec->stats_in);
+ av_free(ctx->avc->streams[i]->codec);
+ av_free(ctx->avc->streams[i]->info);
+ av_free(ctx->avc->streams[i]);
+ }
+
+ if (ctx->twopass_bytebuffer_v) {
+ free_stream(ctx->twopass_bytebuffer_v);
+ ctx->twopass_bytebuffer_v = NULL;
+ }
+
+ if (ctx->twopass_bytebuffer_a) {
+ free_stream(ctx->twopass_bytebuffer_a);
+ ctx->twopass_bytebuffer_a = NULL;
+ }
+
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: encoded %lld bytes\n",
+ ctx->vbytes);
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "ao-lavc: encoded %lld bytes\n",
+ ctx->abytes);
+ if (ctx->avc->pb) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO,
+ "encode-lavc: muxing overhead %lld bytes\n",
+ (long long) (avio_size(ctx->avc->pb) - ctx->vbytes
+ - ctx->abytes));
+ avio_close(ctx->avc->pb);
+ }
+
+ av_free(ctx->avc);
+ }
+
+ ctx->finished = true;
+}
+
+void encode_lavc_set_video_fps(struct encode_lavc_context *ctx, float fps)
+{
+ ctx->vo_fps = fps;
+}
+
+static void encode_2pass_prepare(struct encode_lavc_context *ctx,
+ AVDictionary **dictp,
+ AVStream *stream, struct stream **bytebuf,
+ const char *prefix)
+{
+ if (!*bytebuf) {
+ char buf[sizeof(ctx->avc->filename) + 12];
+ AVDictionaryEntry *de = av_dict_get(ctx->voptions, "flags", NULL, 0);
+
+ snprintf(buf, sizeof(buf), "%s-%s-pass1.log", ctx->avc->filename,
+ prefix);
+ buf[sizeof(buf) - 1] = 0;
+
+ if (value_has_flag(de ? de->value : "", "pass2")) {
+ if (!(*bytebuf = stream_open(buf, NULL))) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "%s: could not open '%s', "
+ "disabling 2-pass encoding at pass 2\n", prefix, buf);
+ stream->codec->flags &= ~CODEC_FLAG_PASS2;
+ set_to_avdictionary(dictp, "flags", "-pass2");
+ } else {
+ struct bstr content = stream_read_complete(*bytebuf, NULL,
+ 1000000000);
+ if (content.start == NULL) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "%s: could not read '%s', "
+ "disabling 2-pass encoding at pass 1\n",
+ prefix, ctx->avc->filename);
+ } else {
+ content.start[content.len] = 0;
+ stream->codec->stats_in = content.start;
+ }
+ free_stream(*bytebuf);
+ *bytebuf = NULL;
+ }
+ }
+
+ if (value_has_flag(de ? de->value : "", "pass1")) {
+ if (!(*bytebuf = open_output_stream(buf, NULL))) {
+ mp_msg(
+ MSGT_ENCODE, MSGL_WARN,
+ "%s: could not open '%s', disabling "
+ "2-pass encoding at pass 1\n",
+ prefix, ctx->avc->filename);
+ set_to_avdictionary(dictp, "flags", "-pass1");
+ }
+ }
+ }
+}
+
+AVStream *encode_lavc_alloc_stream(struct encode_lavc_context *ctx,
+ enum AVMediaType mt)
+{
+ AVDictionaryEntry *de;
+ AVStream *stream = NULL;
+ char **p;
+ int i;
+
+ CHECK_FAIL(ctx, NULL);
+
+ if (ctx->header_written)
+ return NULL;
+
+ for (i = 0; i < ctx->avc->nb_streams; ++i)
+ if (ctx->avc->streams[i]->codec->codec_type == mt)
+ // already have a stream of that type, this cannot really happen
+ return NULL;
+
+ if (ctx->avc->nb_streams == 0) {
+ // if this stream isn't stream #0, allocate a dummy stream first for
+ // the next loop to use
+ if (mt == AVMEDIA_TYPE_VIDEO && ctx->audio_first) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO,
+ "vo-lavc: preallocated audio stream for later use\n");
+ avformat_new_stream(ctx->avc, NULL); // this one is AVMEDIA_TYPE_UNKNOWN for now
+ }
+ if (mt == AVMEDIA_TYPE_AUDIO && ctx->video_first) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO,
+ "ao-lavc: preallocated video stream for later use\n");
+ avformat_new_stream(ctx->avc, NULL); // this one is AVMEDIA_TYPE_UNKNOWN for now
+ }
+ } else {
+ // find possibly preallocated stream
+ for (i = 0; i < ctx->avc->nb_streams; ++i)
+ if (ctx->avc->streams[i]->codec->codec_type == AVMEDIA_TYPE_UNKNOWN) // preallocated stream
+ stream = ctx->avc->streams[i];
+ }
+ if (!stream)
+ stream = avformat_new_stream(ctx->avc, NULL);
+
+ if (ctx->timebase.den == 0) {
+ AVRational r;
+
+ if (ctx->options->fps > 0)
+ r = av_d2q(ctx->options->fps, ctx->options->fps * 1001 + 2);
+ else if (ctx->options->autofps && ctx->vo_fps > 0) {
+ r = av_d2q(ctx->vo_fps, ctx->vo_fps * 1001 + 2);
+ mp_msg(
+ MSGT_ENCODE, MSGL_INFO, "vo-lavc: option --ofps not specified "
+ "but --oautofps is active, using guess of %u/%u\n",
+ (unsigned)r.num, (unsigned)r.den);
+ } else {
+ // we want to handle:
+ // 1/25
+ // 1001/24000
+ // 1001/30000
+ // for this we would need 120000fps...
+ // however, mpeg-4 only allows 16bit values
+ // so let's take 1001/30000 out
+ r.num = 24000;
+ r.den = 1;
+ mp_msg(
+ MSGT_ENCODE, MSGL_INFO, "vo-lavc: option --ofps not specified "
+ "and fps could not be inferred, using guess of %u/%u\n",
+ (unsigned)r.num, (unsigned)r.den);
+ }
+
+ if (ctx->vc && ctx->vc->supported_framerates)
+ r = ctx->vc->supported_framerates[av_find_nearest_q_idx(r,
+ ctx->vc->supported_framerates)];
+
+ ctx->timebase.num = r.den;
+ ctx->timebase.den = r.num;
+ }
+
+ switch (mt) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (!ctx->vc) {
+ encode_lavc_fail(ctx, "vo-lavc: encoder not found\n");
+ return NULL;
+ }
+ avcodec_get_context_defaults3(stream->codec, ctx->vc);
+
+ // stream->time_base = ctx->timebase;
+ // doing this breaks mpeg2ts in ffmpeg
+ // which doesn't properly force the time base to be 90000
+ // furthermore, ffmpeg.c doesn't do this either and works
+
+ stream->codec->time_base = ctx->timebase;
+
+ ctx->voptions = NULL;
+
+ if (ctx->options->vopts)
+ for (p = ctx->options->vopts; *p; ++p)
+ if (!set_to_avdictionary(&ctx->voptions, NULL, *p))
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "vo-lavc: could not set option %s\n", *p);
+
+ de = av_dict_get(ctx->voptions, "global_quality", NULL, 0);
+ if (de)
+ set_to_avdictionary(&ctx->voptions, "flags", "+qscale");
+
+ if (ctx->avc->oformat->flags & AVFMT_GLOBALHEADER)
+ set_to_avdictionary(&ctx->voptions, "flags", "+global_header");
+
+ encode_2pass_prepare(ctx, &ctx->voptions, stream,
+ &ctx->twopass_bytebuffer_v,
+ "vo-lavc");
+ break;
+
+ case AVMEDIA_TYPE_AUDIO:
+ if (!ctx->ac) {
+ encode_lavc_fail(ctx, "ao-lavc: encoder not found\n");
+ return NULL;
+ }
+ avcodec_get_context_defaults3(stream->codec, ctx->ac);
+
+ stream->codec->time_base = ctx->timebase;
+
+ ctx->aoptions = NULL;
+
+ if (ctx->options->aopts)
+ for (p = ctx->options->aopts; *p; ++p)
+ if (!set_to_avdictionary(&ctx->aoptions, NULL, *p))
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "ao-lavc: could not set option %s\n", *p);
+
+ de = av_dict_get(ctx->aoptions, "global_quality", NULL, 0);
+ if (de)
+ set_to_avdictionary(&ctx->aoptions, "flags", "+qscale");
+
+ if (ctx->avc->oformat->flags & AVFMT_GLOBALHEADER)
+ set_to_avdictionary(&ctx->aoptions, "flags", "+global_header");
+
+ encode_2pass_prepare(ctx, &ctx->aoptions, stream,
+ &ctx->twopass_bytebuffer_a,
+ "ao-lavc");
+ break;
+
+ default:
+ encode_lavc_fail(ctx, "encode-lavc: requested invalid stream type\n");
+ return NULL;
+ }
+
+ return stream;
+}
+
+AVCodec *encode_lavc_get_codec(struct encode_lavc_context *ctx,
+ AVStream *stream)
+{
+ CHECK_FAIL(ctx, NULL);
+
+ switch (stream->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ return ctx->vc;
+ case AVMEDIA_TYPE_AUDIO:
+ return ctx->ac;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+int encode_lavc_open_codec(struct encode_lavc_context *ctx, AVStream *stream)
+{
+ AVDictionaryEntry *de;
+ int ret;
+
+ CHECK_FAIL(ctx, -1);
+
+ switch (stream->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Opening video encoder: %s [%s]\n",
+ ctx->vc->long_name, ctx->vc->name);
+
+ if (ctx->vc->capabilities & CODEC_CAP_EXPERIMENTAL) {
+ stream->codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+ mp_msg(MSGT_ENCODE, MSGL_WARN, _(
+ "\n\n"
+ " ********************************************\n"
+ " **** Experimental VIDEO codec selected! ****\n"
+ " ********************************************\n\n"
+ "This means the output file may be broken or bad.\n"
+ "Possible reasons, problems, workarounds:\n"
+ "- Codec implementation in ffmpeg/libav is not finished yet.\n"
+ " Try updating ffmpeg or libav.\n"
+ "- Bad picture quality, blocks, blurriness.\n"
+ " Experiment with codec settings (--ovcopts) to maybe still get the\n"
+ " desired quality output at the expense of bitrate.\n"
+ "- Slow compression.\n"
+ " Bear with it.\n"
+ "- Crashes.\n"
+ " Happens. Try varying options to work around.\n"
+ "If none of this helps you, try another codec in place of %s.\n\n"),
+ ctx->vc->name);
+ }
+
+ ret = avcodec_open2(stream->codec, ctx->vc, &ctx->voptions);
+
+ // complain about all remaining options, then free the dict
+ for (de = NULL; (de = av_dict_get(ctx->voptions, "", de,
+ AV_DICT_IGNORE_SUFFIX));)
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "ovcopts: key '%s' not found.\n",
+ de->key);
+ av_dict_free(&ctx->voptions);
+
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Opening audio encoder: %s [%s]\n",
+ ctx->ac->long_name, ctx->ac->name);
+
+ if (ctx->ac->capabilities & CODEC_CAP_EXPERIMENTAL) {
+ stream->codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+ mp_msg(MSGT_ENCODE, MSGL_WARN, _(
+ "\n\n"
+ " ********************************************\n"
+ " **** Experimental AUDIO codec selected! ****\n"
+ " ********************************************\n\n"
+ "This means the output file may be broken or bad.\n"
+ "Possible reasons, problems, workarounds:\n"
+ "- Codec implementation in ffmpeg/libav is not finished yet.\n"
+ " Try updating ffmpeg or libav.\n"
+ "- Bad sound quality, noise, clicking, whistles, choppiness.\n"
+ " Experiment with codec settings (--oacopts) to maybe still get the\n"
+ " desired quality output at the expense of bitrate.\n"
+ "- Slow compression.\n"
+ " Bear with it.\n"
+ "- Crashes.\n"
+ " Happens. Try varying options to work around.\n"
+ "If none of this helps you, try another codec in place of %s.\n\n"),
+ ctx->ac->name);
+ }
+ ret = avcodec_open2(stream->codec, ctx->ac, &ctx->aoptions);
+
+ // complain about all remaining options, then free the dict
+ for (de = NULL; (de = av_dict_get(ctx->aoptions, "", de,
+ AV_DICT_IGNORE_SUFFIX));)
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "oacopts: key '%s' not found.\n",
+ de->key);
+ av_dict_free(&ctx->aoptions);
+
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ if (ret < 0)
+ encode_lavc_fail(ctx,
+ "unable to open encoder (see above for the cause)");
+
+ return ret;
+}
+
+void encode_lavc_write_stats(struct encode_lavc_context *ctx, AVStream *stream)
+{
+ CHECK_FAIL(ctx, );
+
+ switch (stream->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (ctx->twopass_bytebuffer_v)
+ if (stream->codec->stats_out)
+ stream_write_buffer(ctx->twopass_bytebuffer_v,
+ stream->codec->stats_out,
+ strlen(stream->codec->stats_out));
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (ctx->twopass_bytebuffer_a)
+ if (stream->codec->stats_out)
+ stream_write_buffer(ctx->twopass_bytebuffer_a,
+ stream->codec->stats_out,
+ strlen(stream->codec->stats_out));
+ break;
+ default:
+ break;
+ }
+}
+
+int encode_lavc_write_frame(struct encode_lavc_context *ctx, AVPacket *packet)
+{
+ int r;
+
+ CHECK_FAIL(ctx, -1);
+
+ if (ctx->header_written <= 0)
+ return -1;
+
+ mp_msg(
+ MSGT_ENCODE, MSGL_DBG2,
+ "encode-lavc: write frame: stream %d ptsi %d (%f) dtsi %d (%f) size %d\n",
+ (int)packet->stream_index,
+ (int)packet->pts,
+ packet->pts
+ * (double)ctx->avc->streams[packet->stream_index]->time_base.num
+ / (double)ctx->avc->streams[packet->stream_index]->time_base.den,
+ (int)packet->dts,
+ packet->dts
+ * (double)ctx->avc->streams[packet->stream_index]->time_base.num
+ / (double)ctx->avc->streams[packet->stream_index]->time_base.den,
+ (int)packet->size);
+
+ switch (ctx->avc->streams[packet->stream_index]->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ ctx->vbytes += packet->size;
+ ++ctx->frames;
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ ctx->abytes += packet->size;
+ ctx->audioseconds += packet->duration
+ * (double)ctx->avc->streams[packet->stream_index]->time_base.num
+ / (double)ctx->avc->streams[packet->stream_index]->time_base.den;
+ break;
+ default:
+ break;
+ }
+
+ r = av_interleaved_write_frame(ctx->avc, packet);
+
+ return r;
+}
+
+int encode_lavc_supports_pixfmt(struct encode_lavc_context *ctx,
+ enum PixelFormat pix_fmt)
+{
+ CHECK_FAIL(ctx, 0);
+
+ if (!ctx->vc)
+ return 0;
+ if (pix_fmt == PIX_FMT_NONE)
+ return 0;
+
+ if (!ctx->vc->pix_fmts)
+ return VFCAP_CSP_SUPPORTED;
+ else {
+ const enum PixelFormat *p;
+ for (p = ctx->vc->pix_fmts; *p >= 0; ++p) {
+ if (pix_fmt == *p)
+ return VFCAP_CSP_SUPPORTED;
+ }
+ }
+ return 0;
+}
+
+void encode_lavc_discontinuity(struct encode_lavc_context *ctx)
+{
+ if (!ctx)
+ return;
+
+ CHECK_FAIL(ctx, );
+
+ ctx->audio_pts_offset = MP_NOPTS_VALUE;
+ ctx->last_video_in_pts = MP_NOPTS_VALUE;
+ ctx->discontinuity_pts_offset = MP_NOPTS_VALUE;
+}
+
+static void encode_lavc_printoptions(void *obj, const char *indent,
+ const char *subindent, const char *unit,
+ int filter_and, int filter_eq)
+{
+ const AVOption *opt = NULL;
+ char optbuf[32];
+ while ((opt = av_opt_next(obj, opt))) {
+ // if flags are 0, it simply hasn't been filled in yet and may be
+ // potentially useful
+ if (opt->flags)
+ if ((opt->flags & filter_and) != filter_eq)
+ continue;
+ /* Don't print CONST's on level one.
+ * Don't print anything but CONST's on level two.
+ * Only print items from the requested unit.
+ */
+ if (!unit && opt->type == AV_OPT_TYPE_CONST)
+ continue;
+ else if (unit && opt->type != AV_OPT_TYPE_CONST)
+ continue;
+ else if (unit && opt->type == AV_OPT_TYPE_CONST
+ && strcmp(unit, opt->unit))
+ continue;
+ else if (unit && opt->type == AV_OPT_TYPE_CONST)
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "%s", subindent);
+ else
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "%s", indent);
+
+ switch (opt->type) {
+ case AV_OPT_TYPE_FLAGS:
+ snprintf(optbuf, sizeof(optbuf), "%s=<flags>", opt->name);
+ break;
+ case AV_OPT_TYPE_INT:
+ snprintf(optbuf, sizeof(optbuf), "%s=<int>", opt->name);
+ break;
+ case AV_OPT_TYPE_INT64:
+ snprintf(optbuf, sizeof(optbuf), "%s=<int64>", opt->name);
+ break;
+ case AV_OPT_TYPE_DOUBLE:
+ snprintf(optbuf, sizeof(optbuf), "%s=<double>", opt->name);
+ break;
+ case AV_OPT_TYPE_FLOAT:
+ snprintf(optbuf, sizeof(optbuf), "%s=<float>", opt->name);
+ break;
+ case AV_OPT_TYPE_STRING:
+ snprintf(optbuf, sizeof(optbuf), "%s=<string>", opt->name);
+ break;
+ case AV_OPT_TYPE_RATIONAL:
+ snprintf(optbuf, sizeof(optbuf), "%s=<rational>", opt->name);
+ break;
+ case AV_OPT_TYPE_BINARY:
+ snprintf(optbuf, sizeof(optbuf), "%s=<binary>", opt->name);
+ break;
+ case AV_OPT_TYPE_CONST:
+ snprintf(optbuf, sizeof(optbuf), " [+-]%s", opt->name);
+ break;
+ default:
+ snprintf(optbuf, sizeof(optbuf), "%s", opt->name);
+ break;
+ }
+ optbuf[sizeof(optbuf) - 1] = 0;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "%-32s ", optbuf);
+ if (opt->help)
+ mp_msg(MSGT_ENCODE, MSGL_INFO, " %s", opt->help);
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "\n");
+ if (opt->unit && opt->type != AV_OPT_TYPE_CONST)
+ encode_lavc_printoptions(obj, indent, subindent, opt->unit,
+ filter_and, filter_eq);
+ }
+}
+
+bool encode_lavc_showhelp(struct MPOpts *opts)
+{
+ bool help_output = false;
+ if (av_codec_next(NULL) == NULL)
+ mp_msg(MSGT_ENCODE, MSGL_ERR, "NO CODECS\n");
+#define CHECKS(str) ((str) && \
+ strcmp((str), "help") == 0 ? (help_output |= 1) : 0)
+#define CHECKV(strv) ((strv) && (strv)[0] && \
+ strcmp((strv)[0], "help") == 0 ? (help_output |= 1) : 0)
+ if (CHECKS(opts->encode_output.format)) {
+ AVOutputFormat *c = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Available output formats:\n");
+ while ((c = av_oformat_next(c)))
+ mp_msg(MSGT_ENCODE, MSGL_INFO, " --of=%-13s %s\n", c->name,
+ c->long_name ? c->long_name : "");
+ av_free(c);
+ }
+ if (CHECKV(opts->encode_output.fopts)) {
+ AVFormatContext *c = avformat_alloc_context();
+ AVOutputFormat *format = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO,
+ "Available output format ctx->options:\n");
+ encode_lavc_printoptions(c, " --ofopts=", " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM);
+ av_free(c);
+ while ((format = av_oformat_next(format))) {
+ if (format->priv_class) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Additionally, for --of=%s:\n",
+ format->name);
+ encode_lavc_printoptions(&format->priv_class, " --ofopts=",
+ " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM);
+ }
+ }
+ }
+ if (CHECKV(opts->encode_output.vopts)) {
+ AVCodecContext *c = avcodec_alloc_context3(NULL);
+ AVCodec *codec = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO,
+ "Available output video codec ctx->options:\n");
+ encode_lavc_printoptions(
+ c, " --ovcopts=", " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM |
+ AV_OPT_FLAG_VIDEO_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM |
+ AV_OPT_FLAG_VIDEO_PARAM);
+ av_free(c);
+ while ((codec = av_codec_next(codec))) {
+ if (!av_codec_is_encoder(codec))
+ continue;
+ if (codec->type != AVMEDIA_TYPE_VIDEO)
+ continue;
+ if (opts->encode_output.vcodec && opts->encode_output.vcodec[0] &&
+ strcmp(opts->encode_output.vcodec, codec->name) != 0)
+ continue;
+ if (codec->priv_class) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Additionally, for --ovc=%s:\n",
+ codec->name);
+ encode_lavc_printoptions(
+ &codec->priv_class, " --ovcopts=",
+ " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM |
+ AV_OPT_FLAG_VIDEO_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM |
+ AV_OPT_FLAG_VIDEO_PARAM);
+ }
+ }
+ }
+ if (CHECKV(opts->encode_output.aopts)) {
+ AVCodecContext *c = avcodec_alloc_context3(NULL);
+ AVCodec *codec = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO,