summaryrefslogtreecommitdiffstats
path: root/encode_lavc.c
diff options
context:
space:
mode:
authorRudolf Polzer <divverent@xonotic.org>2012-09-14 17:51:26 +0200
committerwm4 <wm4@nowhere>2012-09-18 21:08:20 +0200
commitf5b8b6ac126d8cef3860db16d3db8e72507a2258 (patch)
treec86a6160cee076d3a632e4d3247e566e8c064390 /encode_lavc.c
parent5617bf483e563aae22100c0ca1d8182f71d4f82d (diff)
downloadmpv-f5b8b6ac126d8cef3860db16d3db8e72507a2258.tar.bz2
mpv-f5b8b6ac126d8cef3860db16d3db8e72507a2258.tar.xz
encode: video encoding now supported using mencoder-like options
Diffstat (limited to 'encode_lavc.c')
-rw-r--r--encode_lavc.c1062
1 files changed, 1062 insertions, 0 deletions
diff --git a/encode_lavc.c b/encode_lavc.c
new file mode 100644
index 0000000000..f8e04f99bc
--- /dev/null
+++ b/encode_lavc.c
@@ -0,0 +1,1062 @@
+/*
+ * Raw video muxing using libavformat
+ * Copyright (C) 2010 Nicolas George <george@nsup.org>
+ * Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.org>
+ *
+ * This file is part of MPlayer.
+ *
+ * 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 "mp_msg.h"
+#include "libmpcodecs/vfcap.h"
+#include "options.h"
+#include "osdep/timer.h"
+#include "libvo/video_out.h"
+#include "talloc.h"
+#include "stream/stream.h"
+
+static int set_to_avdictionary(void *ctx, AVDictionary **dictp, void *octx,
+ const char *str, const char *key_val_sep,
+ const char *pairs_sep)
+{
+ int good = 0;
+ int errorcode = 0;
+ const AVOption *o;
+
+ while (*str) {
+ char *key_ = av_get_token(&str, key_val_sep);
+ char *val_;
+ char *key, *val;
+ char valuebuf[1024];
+
+ if (*key_ && strspn(str, key_val_sep)) {
+ str++;
+ val_ = av_get_token(&str, pairs_sep);
+ } else {
+ av_log(ctx, AV_LOG_ERROR, "Missing key or no key/value "
+ "separator found after key '%s'\n", key_);
+ av_free(key_);
+ if (!errorcode)
+ errorcode = AVERROR(EINVAL);
+ if (*str)
+ ++str;
+ continue;
+ }
+
+ key = key_;
+ val = val_;
+
+ if(!strcmp(key, "qscale") && val[0] != '+' && val[0] != '-' && !av_opt_find(octx, key, NULL, 0, AV_OPT_SEARCH_CHILDREN))
+ {
+ // hack: support "qscale" key as virtual "global_quality" key that multiplies by QP2LAMBDA
+ key = "global_quality";
+ snprintf(valuebuf, sizeof(valuebuf), "(%s)*QP2LAMBDA", val);
+ valuebuf[sizeof(valuebuf)-1] = 0;
+ val = valuebuf;
+ }
+
+ av_log(ctx, AV_LOG_DEBUG, "Setting value '%s' for key '%s'\n",
+ val, key);
+
+ if((o = av_opt_find(octx, key, NULL, 0, AV_OPT_SEARCH_CHILDREN))) {
+ if (av_dict_set(dictp, key, *val ? val : NULL, (o->type == FF_OPT_TYPE_FLAGS && (val[0] == '+' || val[0] == '-')) ? AV_DICT_APPEND : 0) >= 0)
+ ++good;
+ else
+ errorcode = AVERROR(EINVAL);
+ } else {
+ errorcode = AVERROR(ENOENT);
+ }
+
+ av_free(key_);
+ av_free(val_);
+
+ if (*str)
+ ++str;
+ }
+ return errorcode ? errorcode : good;
+}
+
+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;
+
+ 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->vc = avcodec_find_encoder_by_name(tok);
+ ctx->avc->oformat = av_guess_format(tok, ctx->options->file, NULL);
+ av_free(tok);
+ if (ctx->avc->oformat)
+ ctx->vc = NULL;
+ if (ctx->vc)
+ break;
+ if (*in)
+ ++in;
+ }
+ } else {
+ ctx->avc->oformat = av_guess_format(NULL, ctx->options->file, NULL);
+ }
+
+ if (!ctx->avc->oformat) {
+ encode_lavc_fail(ctx, "encode-lavc: format not found\n");
+ return NULL;
+ }
+
+ av_strlcpy(ctx->avc->filename, ctx->options->file,
+ 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->avc, &ctx->foptions, ctx->avc, *p, "=", "")
+ <= 0)
+ 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;
+
+ 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)
+ 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)
+ return 0;
+ }
+
+ ctx->header_written = -1;
+
+ if (!(ctx->avc->oformat->flags & AVFMT_NOFILE)) {
+ 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 = GetTimerMS();
+
+ 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));)
+ av_log(ctx->avc, AV_LOG_ERROR, "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;
+}
+
+static void encode_2pass_prepare(struct encode_lavc_context *ctx, AVDictionary **dictp, void *octx,
+ 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 = open_stream(buf, NULL, 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(stream->codec, dictp, octx, "flags=-pass2", "=", "");
+ } else {
+ struct bstr content = stream_read_complete(*bytebuf, NULL,
+ 1000000000, 1);
+ 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(stream->codec, dictp, octx, "flags=-pass1", "=", "");
+ }
+ }
+ }
+}
+
+AVStream *encode_lavc_alloc_stream(struct encode_lavc_context *ctx,
+ enum AVMediaType mt)
+{
+ AVDictionaryEntry *de;
+ AVStream *stream = NULL;
+ char **p;
+ int i;
+ AVCodecContext *dummy;
+
+ 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->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 && vo_fps > 0) {
+ r = av_d2q(vo_fps, 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;
+ }
+ stream = avformat_new_stream(ctx->avc, 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->codec_id = ctx->vc->id;
+ stream->codec->time_base = ctx->timebase;
+ stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+
+ dummy = avcodec_alloc_context3(ctx->vc);
+ dummy->codec = ctx->vc; // FIXME remove this once we can, caused by a bug in libav, elenril is aware of this
+ // FIXME:
+ // currently, to eradicate this dummy:
+ // add here: stream->codec->codec = ctx->vc; // SAME PROBLEM AS ABOVE
+ // replace dummy by stream->codec
+ // at the end of this block: stream->codec->codec = NULL; // OR SEGV LATER
+
+ ctx->voptions = NULL;
+
+ // libx264: default to preset=medium
+ if (!strcmp(ctx->vc->name, "libx264"))
+ set_to_avdictionary(stream->codec, &ctx->voptions, dummy, "preset=medium", "=", "");
+
+ if (ctx->options->vopts)
+ for (p = ctx->options->vopts; *p; ++p)
+ if (set_to_avdictionary(stream->codec, &ctx->voptions, dummy,
+ *p, "=", "") <= 0)
+ 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(stream->codec, &ctx->voptions, dummy, "flags=+qscale", "=", "");
+
+ if (ctx->avc->oformat->flags & AVFMT_GLOBALHEADER)
+ set_to_avdictionary(stream->codec, &ctx->voptions, dummy, "flags=+global_header", "=", "");
+
+ encode_2pass_prepare(ctx, &ctx->voptions, dummy, stream, &ctx->twopass_bytebuffer_v,
+ "vo-lavc");
+
+ av_free(dummy);
+ break;
+
+ case AVMEDIA_TYPE_AUDIO:
+ if (!ctx->ac) {
+ encode_lavc_fail(ctx, "ao-lavc: encoder not found\n");
+ return NULL;
+ }
+ stream = avformat_new_stream(ctx->avc, ctx->ac);
+
+ stream->codec->codec_id = ctx->ac->id;
+ stream->codec->time_base = ctx->timebase;
+ stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+
+ dummy = avcodec_alloc_context3(ctx->ac);
+ dummy->codec = ctx->ac; // FIXME remove this once we can, caused by a bug in libav, elenril is aware of this
+ // FIXME:
+ // currently, to eradicate this dummy:
+ // add here: stream->codec->codec = ctx->ac; // SAME PROBLEM AS ABOVE
+ // replace dummy by stream->codec
+ // at the end of this block: stream->codec->codec = NULL; // OR SEGV LATER
+
+ ctx->aoptions = NULL;
+
+ if (ctx->options->aopts)
+ for (p = ctx->options->aopts; *p; ++p)
+ if (set_to_avdictionary(stream->codec, &ctx->aoptions, dummy,
+ *p, "=", "") <= 0)
+ 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(stream->codec, &ctx->aoptions, dummy, "flags=+qscale", "=", "");
+
+ if (ctx->avc->oformat->flags & AVFMT_GLOBALHEADER)
+ set_to_avdictionary(stream->codec, &ctx->aoptions, dummy, "flags=+global_header", "=", "");
+
+ encode_2pass_prepare(ctx, &ctx->aoptions, dummy, stream, &ctx->twopass_bytebuffer_a,
+ "ao-lavc");
+
+ av_free(dummy);
+ 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:
+ if (ctx->vc->capabilities & CODEC_CAP_EXPERIMENTAL) {
+ stream->codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+ av_log(ctx->avc, AV_LOG_ERROR, _(
+ "\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));)
+ av_log(ctx->avc, AV_LOG_ERROR, "Key '%s' not found.\n", de->key);
+ av_dict_free(&ctx->voptions);
+
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+ if (ctx->ac->capabilities & CODEC_CAP_EXPERIMENTAL) {
+ stream->codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+ av_log(ctx->avc, AV_LOG_ERROR, _(
+ "\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));)
+ av_log(ctx->avc, AV_LOG_ERROR, "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) 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->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;
+ 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 == FF_OPT_TYPE_CONST)
+ continue;
+ else if (unit && opt->type != FF_OPT_TYPE_CONST)
+ continue;
+ else if (unit && opt->type == FF_OPT_TYPE_CONST
+ && strcmp(unit, opt->unit))
+ continue;
+ else if (unit && opt->type == FF_OPT_TYPE_CONST)
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "%s", subindent);
+ else
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "%s", indent);
+
+ switch (opt->type) {
+ case FF_OPT_TYPE_FLAGS:
+ snprintf(optbuf, sizeof(optbuf), "%s=<flags>", opt->name);
+ break;
+ case FF_OPT_TYPE_INT:
+ snprintf(optbuf, sizeof(optbuf), "%s=<int>", opt->name);
+ break;
+ case FF_OPT_TYPE_INT64:
+ snprintf(optbuf, sizeof(optbuf), "%s=<int64>", opt->name);
+ break;
+ case FF_OPT_TYPE_DOUBLE:
+ snprintf(optbuf, sizeof(optbuf), "%s=<double>", opt->name);
+ break;
+ case FF_OPT_TYPE_FLOAT:
+ snprintf(optbuf, sizeof(optbuf), "%s=<float>", opt->name);
+ break;
+ case FF_OPT_TYPE_STRING:
+ snprintf(optbuf, sizeof(optbuf), "%s=<string>", opt->name);
+ break;
+ case FF_OPT_TYPE_RATIONAL:
+ snprintf(optbuf, sizeof(optbuf), "%s=<rational>", opt->name);
+ break;
+ case FF_OPT_TYPE_BINARY:
+ snprintf(optbuf, sizeof(optbuf), "%s=<binary>", opt->name);
+ break;
+ case FF_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 != FF_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,
+ "Available output audio codec ctx->options:\n");
+ encode_lavc_printoptions(c, " -oacopts ", " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM);
+ av_free(c);
+ while ((codec = av_codec_next(codec))) {
+ if (!av_codec_is_encoder(codec))
+ continue;
+ if (codec->type != AVMEDIA_TYPE_AUDIO)
+ continue;
+ if (opts->encode_output.acodec && opts->encode_output.acodec[0] && strcmp(opts->encode_output.acodec, codec->name) != 0)
+ continue;
+ if (codec->priv_class) {
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Additionally, for -oac %s:\n",
+ codec->name);
+ encode_lavc_printoptions(&codec->priv_class, " -oacopts ",
+ " ", NULL,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM,
+ AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM);
+ }
+ }
+ }
+ if (CHECKS(opts->encode_output.vcodec)) {
+ AVCodec *c = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Available output video codecs:\n");
+ while ((c = av_codec_next(c))) {
+ if (!av_codec_is_encoder(c))
+ continue;
+ if (c->type != AVMEDIA_TYPE_VIDEO)
+ continue;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, " -ovc %-12s %s\n", c->name,
+ c->long_name ? c->long_name : "");
+ }
+ av_free(c);
+ }
+ if (CHECKS(opts->encode_output.acodec)) {
+ AVCodec *c = NULL;
+ mp_msg(MSGT_ENCODE, MSGL_INFO, "Available output audio codecs:\n");
+ while ((c = av_codec_next(c))) {
+ if (!av_codec_is_encoder(c))