diff options
author | Rudolf Polzer <divverent@xonotic.org> | 2012-09-14 17:51:26 +0200 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2012-09-18 21:08:20 +0200 |
commit | f5b8b6ac126d8cef3860db16d3db8e72507a2258 (patch) | |
tree | c86a6160cee076d3a632e4d3247e566e8c064390 /encode_lavc.c | |
parent | 5617bf483e563aae22100c0ca1d8182f71d4f82d (diff) | |
download | mpv-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.c | 1062 |
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-> |