diff options
-rw-r--r-- | DOCS/man/encode.rst | 15 | ||||
-rw-r--r-- | audio/out/ao_lavc.c | 240 | ||||
-rw-r--r-- | common/common.h | 11 | ||||
-rw-r--r-- | common/encode.h | 11 | ||||
-rw-r--r-- | common/encode_lavc.c | 1136 | ||||
-rw-r--r-- | common/encode_lavc.h | 105 | ||||
-rw-r--r-- | etc/encoding-profiles.conf | 6 | ||||
-rw-r--r-- | player/loadfile.c | 18 | ||||
-rw-r--r-- | player/main.c | 7 | ||||
-rw-r--r-- | player/video.c | 7 | ||||
-rw-r--r-- | video/out/vo_lavc.c | 312 |
11 files changed, 754 insertions, 1114 deletions
diff --git a/DOCS/man/encode.rst b/DOCS/man/encode.rst index 0edf7e7215..9169f4781b 100644 --- a/DOCS/man/encode.rst +++ b/DOCS/man/encode.rst @@ -8,9 +8,8 @@ You can encode files from one format/codec to another using this facility. ``--of=<format>`` Specifies the output format (overrides autodetection by the file name - extension of the file specified by ``-o``). This can be a comma separated - list of possible formats to try. See ``--of=help`` for a full list of - supported formats. + extension of the file specified by ``-o``). See ``--of=help`` for a full + list of supported formats. ``--ofopts=<options>`` Specifies the output format options for libavformat. @@ -59,9 +58,8 @@ You can encode files from one format/codec to another using this facility. avoid ``--oautofps``. ``--oac=<codec>`` - Specifies the output audio codec. This can be a comma separated list of - possible codecs to try. See ``--oac=help`` for a full list of supported - codecs. + Specifies the output audio codec. See ``--oac=help`` for a full list of + supported codecs. ``--oaoffset=<value>`` Shifts audio data by the given time (in seconds) by adding/removing @@ -97,9 +95,8 @@ You can encode files from one format/codec to another using this facility. By default, the order is unspecified. Deprecated. ``--ovc=<codec>`` - Specifies the output video codec. This can be a comma separated list of - possible codecs to try. See ``--ovc=help`` for a full list of supported - codecs. + Specifies the output video codec. See ``--ovc=help`` for a full list of + supported codecs. ``--ovoffset=<value>`` Shifts video data by the given time (in seconds) by shifting the pts diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c index aaa9e6422c..e18db667a3 100644 --- a/audio/out/ao_lavc.c +++ b/audio/out/ao_lavc.c @@ -40,8 +40,8 @@ #include "common/encode_lavc.h" struct priv { - AVStream *stream; - AVCodecContext *codec; + struct encoder_context *enc; + int pcmhack; int aframesize; int aframecount; @@ -53,14 +53,13 @@ struct priv { double expected_next_pts; AVRational worst_time_base; - int worst_time_base_is_stream; bool shutdown; }; static void encode(struct ao *ao, double apts, void **data); -static bool supports_format(AVCodec *codec, int format) +static bool supports_format(const AVCodec *codec, int format) { for (const enum AVSampleFormat *sampleformat = codec->sample_fmts; sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE; @@ -72,7 +71,7 @@ static bool supports_format(AVCodec *codec, int format) return false; } -static void select_format(struct ao *ao, AVCodec *codec) +static void select_format(struct ao *ao, const AVCodec *codec) { int formats[AF_FORMAT_COUNT + 1]; af_get_best_sample_formats(ao->format, formats); @@ -88,70 +87,53 @@ static void select_format(struct ao *ao, AVCodec *codec) // open & setup audio device static int init(struct ao *ao) { - struct priv *ac = talloc_zero(ao, struct priv); - AVCodec *codec; - - ao->priv = ac; + struct priv *ac = ao->priv; - if (!encode_lavc_available(ao->encode_lavc_ctx)) { - MP_ERR(ao, "the option --o (output file) must be specified\n"); + ac->enc = encoder_context_alloc(ao->encode_lavc_ctx, STREAM_AUDIO, ao->log); + if (!ac->enc) return -1; - } + talloc_steal(ac, ac->enc); - pthread_mutex_lock(&ao->encode_lavc_ctx->lock); - - if (encode_lavc_alloc_stream(ao->encode_lavc_ctx, - AVMEDIA_TYPE_AUDIO, - &ac->stream, &ac->codec) < 0) - { - MP_ERR(ao, "could not get a new audio stream\n"); - goto fail; - } - - codec = ao->encode_lavc_ctx->ac; + AVCodecContext *encoder = ac->enc->encoder; + const AVCodec *codec = encoder->codec; int samplerate = af_select_best_samplerate(ao->samplerate, codec->supported_samplerates); if (samplerate > 0) ao->samplerate = samplerate; - // TODO: Remove this redundancy with encode_lavc_alloc_stream also - // setting the time base. - // Using codec->time_base is deprecated, but needed for older lavf. - ac->stream->time_base.num = 1; - ac->stream->time_base.den = ao->samplerate; - ac->codec->time_base.num = 1; - ac->codec->time_base.den = ao->samplerate; + encoder->time_base.num = 1; + encoder->time_base.den = ao->samplerate; - ac->codec->sample_rate = ao->samplerate; + encoder->sample_rate = ao->samplerate; struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_any(&sel); if (!ao_chmap_sel_adjust2(ao, &sel, &ao->channels, false)) goto fail; mp_chmap_reorder_to_lavc(&ao->channels); - ac->codec->channels = ao->channels.num; - ac->codec->channel_layout = mp_chmap_to_lavc(&ao->channels); + encoder->channels = ao->channels.num; + encoder->channel_layout = mp_chmap_to_lavc(&ao->channels); - ac->codec->sample_fmt = AV_SAMPLE_FMT_NONE; + encoder->sample_fmt = AV_SAMPLE_FMT_NONE; select_format(ao, codec); ac->sample_size = af_fmt_to_bytes(ao->format); - ac->codec->sample_fmt = af_to_avformat(ao->format); - ac->codec->bits_per_raw_sample = ac->sample_size * 8; + encoder->sample_fmt = af_to_avformat(ao->format); + encoder->bits_per_raw_sample = ac->sample_size * 8; - if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->codec) < 0) + if (!encoder_init_codec_and_muxer(ac->enc)) goto fail; ac->pcmhack = 0; - if (ac->codec->frame_size <= 1) - ac->pcmhack = av_get_bits_per_sample(ac->codec->codec_id) / 8; + if (encoder->frame_size <= 1) + ac->pcmhack = av_get_bits_per_sample(encoder->codec_id) / 8; if (ac->pcmhack) { ac->aframesize = 16384; // "enough" } else { - ac->aframesize = ac->codec->frame_size; + ac->aframesize = encoder->frame_size; } // enough frames for at least 0.25 seconds @@ -169,7 +151,6 @@ static int init(struct ao *ao) if (ao->channels.num > AV_NUM_DATA_POINTERS) goto fail; - pthread_mutex_unlock(&ao->encode_lavc_ctx->lock); return 0; fail: @@ -184,28 +165,17 @@ static void uninit(struct ao *ao) struct priv *ac = ao->priv; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; - if (!ac || ac->shutdown) - return; - - pthread_mutex_lock(&ectx->lock); + if (!ac->shutdown) { + double outpts = ac->expected_next_pts; - if (!encode_lavc_start(ectx)) { - MP_WARN(ao, "not even ready to encode audio at end -> dropped\n"); + pthread_mutex_lock(&ectx->lock); + if (!ac->enc->options->rawts && ac->enc->options->copyts) + outpts += ectx->discontinuity_pts_offset; pthread_mutex_unlock(&ectx->lock); - return; - } - if (ac->stream) { - double outpts = ac->expected_next_pts; - if (!ectx->options->rawts && ectx->options->copyts) - outpts += ectx->discontinuity_pts_offset; - outpts += encode_lavc_getoffset(ectx, ac->codec); + outpts += encoder_get_offset(ac->enc); encode(ao, outpts, NULL); } - - pthread_mutex_unlock(&ectx->lock); - - ac->shutdown = true; } // return: how many samples can be played without blocking @@ -216,106 +186,21 @@ static int get_space(struct ao *ao) return ac->aframesize * ac->framecount; } -static void write_packet(struct ao *ao, AVPacket *packet) -{ - // TODO: Can we unify this with the equivalent video code path? - struct priv *ac = ao->priv; - - packet->stream_index = ac->stream->index; - if (packet->pts != AV_NOPTS_VALUE) { - packet->pts = av_rescale_q(packet->pts, - ac->codec->time_base, - ac->stream->time_base); - } else { - // Do we need this at all? Better be safe than sorry... - MP_WARN(ao, "encoder lost pts, why?\n"); - if (ac->savepts != MP_NOPTS_VALUE) { - packet->pts = av_rescale_q(ac->savepts, - ac->codec->time_base, - ac->stream->time_base); - } - } - if (packet->dts != AV_NOPTS_VALUE) { - packet->dts = av_rescale_q(packet->dts, - ac->codec->time_base, - ac->stream->time_base); - } - if (packet->duration > 0) { - packet->duration = av_rescale_q(packet->duration, - ac->codec->time_base, - ac->stream->time_base); - } - - ac->savepts = AV_NOPTS_VALUE; - - if (encode_lavc_write_frame(ao->encode_lavc_ctx, ac->stream, packet) < 0) { - MP_ERR(ao, "error writing at %d %d/%d\n", - (int) packet->pts, - ac->stream->time_base.num, - ac->stream->time_base.den); - return; - } -} - -static void encode_audio_and_write(struct ao *ao, AVFrame *frame) -{ - // TODO: Can we unify this with the equivalent video code path? - struct priv *ac = ao->priv; - AVPacket packet = {0}; - - int status = avcodec_send_frame(ac->codec, frame); - if (status < 0) { - MP_ERR(ao, "error encoding at %d %d/%d\n", - frame ? (int) frame->pts : -1, - ac->codec->time_base.num, - ac->codec->time_base.den); - return; - } - - for (;;) { - av_init_packet(&packet); - status = avcodec_receive_packet(ac->codec, &packet); - if (status == AVERROR(EAGAIN)) { // No more packets for now. - if (frame == NULL) { - MP_ERR(ao, "sent flush frame, got EAGAIN"); - } - break; - } - if (status == AVERROR_EOF) { // No more packets, ever. - if (frame != NULL) { - MP_ERR(ao, "sent audio frame, got EOF"); - } - break; - } - if (status < 0) { - MP_ERR(ao, "error encoding at %d %d/%d\n", - frame ? (int) frame->pts : -1, - ac->codec->time_base.num, - ac->codec->time_base.den); - break; - } - if (frame) { - if (ac->savepts == AV_NOPTS_VALUE) - ac->savepts = frame->pts; - } - encode_lavc_write_stats(ao->encode_lavc_ctx, ac->codec); - write_packet(ao, &packet); - av_packet_unref(&packet); - } -} - // must get exactly ac->aframesize amount of data static void encode(struct ao *ao, double apts, void **data) { struct priv *ac = ao->priv; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; + AVCodecContext *encoder = ac->enc->encoder; double realapts = ac->aframecount * (double) ac->aframesize / ao->samplerate; ac->aframecount++; + pthread_mutex_lock(&ectx->lock); if (data) ectx->audio_pts_offset = realapts - apts; + pthread_mutex_unlock(&ectx->lock); if(data) { AVFrame *frame = av_frame_alloc(); @@ -329,17 +214,17 @@ static void encode(struct ao *ao, double apts, void **data) frame->linesize[0] = frame->nb_samples * ao->sstride; - if (ectx->options->rawts || ectx->options->copyts) { + if (ac->enc->options->rawts || ac->enc->options->copyts) { // real audio pts - frame->pts = floor(apts * ac->codec->time_base.den / - ac->codec->time_base.num + 0.5); + frame->pts = floor(apts * encoder->time_base.den / + encoder->time_base.num + 0.5); } else { // audio playback time - frame->pts = floor(realapts * ac->codec->time_base.den / - ac->codec->time_base.num + 0.5); + frame->pts = floor(realapts * encoder->time_base.den / + encoder->time_base.num + 0.5); } - int64_t frame_pts = av_rescale_q(frame->pts, ac->codec->time_base, + int64_t frame_pts = av_rescale_q(frame->pts, encoder->time_base, ac->worst_time_base); if (ac->lastpts != AV_NOPTS_VALUE && frame_pts <= ac->lastpts) { // this indicates broken video @@ -348,15 +233,15 @@ static void encode(struct ao *ao, double apts, void **data) (int)frame->pts, (int)ac->lastpts); frame_pts = ac->lastpts + 1; frame->pts = av_rescale_q(frame_pts, ac->worst_time_base, - ac->codec->time_base); + encoder->time_base); } ac->lastpts = frame_pts; - frame->quality = ac->codec->global_quality; - encode_audio_and_write(ao, frame); + frame->quality = encoder->global_quality; + encoder_encode(ac->enc, frame); av_frame_free(&frame); } else { - encode_audio_and_write(ao, NULL); + encoder_encode(ac->enc, NULL); } } @@ -365,20 +250,16 @@ static void encode(struct ao *ao, double apts, void **data) static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *ac = ao->priv; + struct encoder_context *enc = ac->enc; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; int bufpos = 0; double nextpts; double outpts; int orig_samples = samples; + // for ectx PTS fields pthread_mutex_lock(&ectx->lock); - if (!encode_lavc_start(ectx)) { - MP_WARN(ao, "not ready yet for encoding audio\n"); - pthread_mutex_unlock(&ectx->lock); - return 0; - } - double pts = ectx->last_audio_in_pts; pts += ectx->samples_since_last_pts / (double)ao->samplerate; @@ -407,26 +288,10 @@ static int play(struct ao *ao, void **data, int samples, int flags) } if (ac->worst_time_base.den == 0) { - if (ac->codec->time_base.num * (double) ac->stream->time_base.den >= - ac->stream->time_base.num * (double) ac->codec->time_base.den) { - MP_VERBOSE(ao, "NOTE: using codec time base (%d/%d) for pts " - "adjustment; the stream base (%d/%d) is not worse.\n", - (int)ac->codec->time_base.num, - (int)ac->codec->time_base.den, - (int)ac->stream->time_base.num, - (int)ac->stream->time_base.den); - ac->worst_time_base = ac->codec->time_base; - ac->worst_time_base_is_stream = 0; - } else { - MP_WARN(ao, "NOTE: not using codec time base (%d/%d) for pts " - "adjustment; the stream base (%d/%d) is worse.\n", - (int)ac->codec->time_base.num, - (int)ac->codec->time_base.den, - (int)ac->stream->time_base.num, - (int)ac->stream->time_base.den); - ac->worst_time_base = ac->stream->time_base; - ac->worst_time_base_is_stream = 1; - } + // We don't know the muxer time_base anymore, and can't, because we + // might start encoding before the muxer is opened. (The muxer decides + // the final AVStream.time_base when opening the muxer.) + ac->worst_time_base = enc->encoder->time_base; // NOTE: we use the following "axiom" of av_rescale_q: // if time base A is worse than time base B, then @@ -446,7 +311,7 @@ static int play(struct ao *ao, void **data, int samples, int flags) } // Fix and apply the discontinuity pts offset. - if (!ectx->options->rawts && ectx->options->copyts) { + if (!enc->options->rawts && enc->options->copyts) { // fix the discontinuity pts offset nextpts = pts; if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) { @@ -465,8 +330,10 @@ static int play(struct ao *ao, void **data, int samples, int flags) outpts = pts; } + pthread_mutex_unlock(&ectx->lock); + // Shift pts by the pts offset first. - outpts += encode_lavc_getoffset(ectx, ac->codec); + outpts += encoder_get_offset(enc); while (samples - bufpos >= ac->aframesize) { void *start[MP_NUM_CHANNELS] = {0}; @@ -479,8 +346,10 @@ static int play(struct ao *ao, void **data, int samples, int flags) // Calculate expected pts of next audio frame (input side). ac->expected_next_pts = pts + bufpos / (double) ao->samplerate; + pthread_mutex_lock(&ectx->lock); + // Set next allowed input pts value (input side). - if (!ectx->options->rawts && ectx->options->copyts) { + if (!enc->options->rawts && enc->options->copyts) { nextpts = ac->expected_next_pts + ectx->discontinuity_pts_offset; if (nextpts > ectx->next_in_pts) ectx->next_in_pts = nextpts; @@ -513,6 +382,7 @@ const struct ao_driver audio_out_lavc = { .encode = true, .description = "audio encoding using libavcodec", .name = "lavc", + .priv_size = sizeof(struct priv), .init = init, .uninit = uninit, .get_space = get_space, diff --git a/common/common.h b/common/common.h index 14a9973371..dfb2ba7b7a 100644 --- a/common/common.h +++ b/common/common.h @@ -106,4 +106,15 @@ char *mp_tprintf_buf(char *buf, size_t buf_size, const char *format, ...) char **mp_dup_str_array(void *tctx, char **s); +// We generally do not handle allocation failure of small malloc()s. This would +// create a large number of rarely tested code paths, which would probably +// regress and cause security issues. We prefer to fail fast. +// This macro generally behaves like an assert(), except it will make sure to +// kill the process even with NDEBUG. +#define MP_HANDLE_OOM(x) do { \ + assert(x); \ + if (!(x)) \ + abort(); \ + } while (0) + #endif /* MPLAYER_MPCOMMON_H */ diff --git a/common/encode.h b/common/encode.h index c29cb3bc67..fcf4a8317e 100644 --- a/common/encode.h +++ b/common/encode.h @@ -54,17 +54,16 @@ struct encode_opts { char **remove_metadata; }; -// interface for mplayer.c -struct encode_lavc_context *encode_lavc_init(struct encode_opts *options, - struct mpv_global *global); -void encode_lavc_free(struct encode_lavc_context *ctx); +// interface for player core +struct encode_lavc_context *encode_lavc_init(struct mpv_global *global); +bool encode_lavc_free(struct encode_lavc_context *ctx); void encode_lavc_discontinuity(struct encode_lavc_context *ctx); bool encode_lavc_showhelp(struct mp_log *log, struct encode_opts *options); int encode_lavc_getstatus(struct encode_lavc_context *ctx, char *buf, int bufsize, float relative_position); -void encode_lavc_expect_stream(struct encode_lavc_context *ctx, int mt); +void encode_lavc_expect_stream(struct encode_lavc_context *ctx, + enum stream_type type); void encode_lavc_set_metadata(struct encode_lavc_context *ctx, struct mp_tags *metadata); -void encode_lavc_set_video_fps(struct encode_lavc_context *ctx, float fps); void encode_lavc_set_audio_pts(struct encode_lavc_context *ctx, double pts); bool encode_lavc_didfail(struct encode_lavc_context *ctx); // check if encoding failed diff --git a/common/encode_lavc.c b/common/encode_lavc.c index 905c30dc54..6f8378ac7f 100644 --- a/common/encode_lavc.c +++ b/common/encode_lavc.c @@ -21,6 +21,7 @@ */ #include <libavutil/avutil.h> +#include <libavutil/timestamp.h> #include "config.h" #include "encode_lavc.h" @@ -28,6 +29,7 @@ #include "common/global.h" #include "common/msg.h" #include "common/msg_control.h" +#include "options/m_config.h" #include "options/m_option.h" #include "options/options.h" #include "osdep/timer.h" @@ -35,6 +37,44 @@ #include "mpv_talloc.h" #include "stream/stream.h" +struct encode_priv { + struct mp_log *log; + + // --- All fields are protected by encode_lavc_context.lock + + bool failed; + + struct mp_tags *metadata; + + AVFormatContext *muxer; + + bool header_written; // muxer was initialized + + struct mux_stream **streams; + int num_streams; + + // Temporary queue while muxer is not initialized. + AVPacket **packets; + int num_packets; + + // Statistics + double t0; + + long long abytes; + long long vbytes; + + unsigned int frames; + double audioseconds; +}; + +struct mux_stream { + int index; // index of this into p->streams[] + struct encode_lavc_context *ctx; + enum AVMediaType codec_type; + AVRational encoder_timebase; // packet timestamps from encoder + AVStream *st; +}; + #define OPT_BASE_STRUCT struct encode_opts const struct m_sub_options encode_config = { .opts = (const m_option_t[]) { @@ -71,53 +111,23 @@ const struct m_sub_options encode_config = { }, }; -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)) { \ - MP_ERR(ctx, \ - "Called a function on a %s encoding context. Bailing out.\n", \ - ctx->failed ? "failed" : "finished"); \ - return val; \ - } +static void write_remaining_packets(struct encode_lavc_context *ctx); -#define CHECK_FAIL_UNLOCK(ctx, val) \ - if (ctx && (ctx->failed)) { \ - MP_ERR(ctx, \ - "Called a function on a %s encoding context. Bailing out.\n", \ - ctx->failed ? "failed" : "finished"); \ - pthread_mutex_unlock(&ctx->lock); \ - return val; \ - } - -int encode_lavc_available(struct encode_lavc_context *ctx) +struct encode_lavc_context *encode_lavc_init(struct mpv_global *global) { - CHECK_FAIL(ctx, 0); - return ctx && ctx->avc; -} + struct encode_lavc_context *ctx = talloc_ptrtype(NULL, ctx); + *ctx = (struct encode_lavc_context){ + .global = global, + .options = mp_get_config_group(ctx, global, &encode_config), + .priv = talloc_zero(ctx, struct encode_priv), + .log = mp_log_new(ctx, global->log, "encode"), + }; + pthread_mutex_init(&ctx->lock, NULL); -struct encode_lavc_context *encode_lavc_init(struct encode_opts *options, - struct mpv_global *global) -{ - struct encode_lavc_context *ctx; - const char *filename = options->file; + struct encode_priv *p = ctx->priv; + p->log = ctx->log; + + const char *filename = ctx->options->file; // STUPID STUPID STUPID STUPID avio // does not support "-" as file name to mean stdin/stdout @@ -131,109 +141,46 @@ struct encode_lavc_context *encode_lavc_init(struct encode_opts *options, !strcmp(filename, "pipe:1"))) mp_msg_force_stderr(global, true); - ctx = talloc_zero(NULL, struct encode_lavc_context); - pthread_mutex_init(&ctx->lock, NULL); - ctx->log = mp_log_new(ctx, global->log, "encode-lavc"); - ctx->global = global; 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, "format not found\n"); - return NULL; - } + p->muxer = avformat_alloc_context(); + MP_HANDLE_OOM(p->muxer); - ctx->avc->url = av_strdup(filename); - - mp_set_avdict(&ctx->foptions, ctx->options->fopts); - - 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; - } + if (ctx->options->format && ctx->options->format[0]) { + ctx->oformat = av_guess_format(ctx->options->format, filename, NULL); } else { - ctx->vc = avcodec_find_encoder(av_guess_codec(ctx->avc->oformat, NULL, - ctx->avc->url, NULL, - AVMEDIA_TYPE_VIDEO)); + ctx->oformat = av_guess_format(NULL, filename, NULL); } - 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->url, NULL, - AVMEDIA_TYPE_AUDIO)); - } - - if (!ctx->vc && !ctx->ac) { - encode_lavc_fail(ctx, "neither audio nor video codec was found\n"); - return NULL; + if (!ctx->oformat) { + MP_FATAL(ctx, "format not found\n"); + goto fail; } - /* taken from ffmpeg unchanged - * TODO turn this into an option if anyone needs this */ + p->muxer->oformat = ctx->oformat; - 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; + p->muxer->url = av_strdup(filename); + MP_HANDLE_OOM(p->muxer->url); return ctx; + +fail: + p->failed = true; + encode_lavc_free(ctx); + return NULL; } void encode_lavc_set_metadata(struct encode_lavc_context *ctx, struct mp_tags *metadata) { + struct encode_priv *p = ctx->priv; + + pthread_mutex_lock(&ctx->lock); + if (ctx->options->copy_metadata) { - ctx->metadata = metadata; + p->metadata = mp_tags_dup(ctx, metadata); } else { - ctx->metadata = talloc_zero(ctx, struct mp_tags); + p->metadata = talloc_zero(ctx, struct mp_tags); } if (ctx->options->set_metadata) { @@ -242,7 +189,7 @@ void encode_lavc_set_metadata(struct encode_lavc_context *ctx, for (int n = 0; kv[n * 2]; n++) { MP_VERBOSE(ctx, "setting metadata value '%s' for key '%s'\n", kv[n*2 + 0], kv[n*2 +1]); - mp_tags_set_str(ctx->metadata, kv[n*2 + 0], kv[n*2 +1]); + mp_tags_set_str(p->metadata, kv[n*2 + 0], kv[n*2 +1]); } } @@ -251,152 +198,52 @@ void encode_lavc_set_metadata(struct encode_lavc_context *ctx, // Remove all user-provided metadata tags for (int n = 0; k[n]; n++) { MP_VERBOSE(ctx, "removing metadata key '%s'\n", k[n]); - mp_tags_remove_str(ctx->metadata, k[n]); + mp_tags_remove_str(p->metadata, k[n]); } } + + pthread_mutex_unlock(&ctx->lock); } -int encode_lavc_start(struct encode_lavc_context *ctx) +bool encode_lavc_free(struct encode_lavc_context *ctx) { - AVDictionaryEntry *de; - - if (ctx->header_written < 0) - return 0; - if (ctx->header_written > 0) - return 1; - - CHECK_FAIL(ctx, 0); - - if (ctx->expect_video && ctx->vcc == NULL) { - if (ctx->avc->oformat->video_codec != AV_CODEC_ID_NONE || - ctx->options->vcodec) - { - encode_lavc_fail(ctx,"no video stream succeeded - invalid codec?\n"); - return 0; - } - } - - if (ctx->expect_audio && ctx->acc == NULL) { - if (ctx->avc->oformat->audio_codec != AV_CODEC_ID_NONE || - ctx->options->acodec) - { - encode_lavc_fail(ctx, "no audio stream succeeded - invalid codec?\n"); - return 0; - } - } - - ctx->header_written = -1; + bool res = true; + if (!ctx) + return res; - if (!(ctx->avc->oformat->flags & AVFMT_NOFILE)) { - MP_INFO(ctx, "Opening output file: %s\n", ctx->avc->url); + struct encode_priv *p = ctx->priv; - if (avio_open(&ctx->avc->pb, ctx->avc->url, AVIO_FLAG_WRITE) < 0) { - encode_lavc_fail(ctx, "could not open '%s'\n", - ctx->avc->url); - return 0; - } + if (!p->failed && !p->header_written) { + MP_FATAL(p, "no data written to target file\n"); + p->failed = true; } - ctx->t0 = mp_time_sec(); + write_remaining_packets(ctx); - MP_INFO(ctx, "Opening muxer: %s [%s]\n", - ctx->avc->oformat->long_name, ctx->avc->oformat->name); + if (!p->failed && p->header_written) { + if (av_write_trailer(p->muxer) < 0) + MP_ERR(p, "error writing trailer\n"); - if (ctx->metadata) { - for (int i = 0; i < ctx->metadata->num_keys; i++) { - av_dict_set(&ctx->avc->metadata, - ctx->metadata->keys[i], ctx->metadata->values[i], 0); - } - } + MP_INFO(p, "video: encoded %lld bytes\n", p->vbytes); + MP_INFO(p, "audio: encoded %lld bytes\n", p->abytes); - if (avformat_write_header(ctx->avc, &ctx->foptions) < 0) { - encode_lavc_fail(ctx, "could not write header\n"); - return 0; + MP_INFO(p, "muxing overhead %lld bytes\n", + (long long)(avio_size(p->muxer->pb) - p->vbytes - p->abytes)); } - for (de = NULL; (de = av_dict_get(ctx->foptions, "", de, - AV_DICT_IGNORE_SUFFIX));) - { - MP_WARN(ctx, "ofopts: key '%s' not found.\n", de->key); + if (avio_closep(&p->muxer->pb) < 0 && !p->failed) { + MP_ERR(p, "Closing file failed\n"); + p->failed = true; } - av_dict_free(&ctx->foptions); - ctx->header_written = 1; - return 1; -} + avformat_free_context(p->muxer); -void encode_lavc_free(struct encode_lavc_context *ctx) -{ - unsigned i; - - if (!ctx) - return; - - if (ctx->avc) { - if (ctx->header_written > 0) - av_write_trailer(ctx->avc); // this is allowed to fail - - if (ctx->vcc) { - if (ctx->twopass_bytebuffer_v) { - char *stats = ctx->vcc->stats_out; - if (stats) - stream_write_buffer(ctx->twopass_bytebuffer_v, - stats, strlen(stats)); - } - avcodec_free_context(&ctx->vcc); - } - - if (ctx->acc) { - if (ctx->twopass_bytebuffer_a) { - char *stats = ctx->acc->stats_out; - if (stats) - stream_write_buffer(ctx->twopass_bytebuffer_a, - |