summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2018-04-22 19:40:36 +0200
committerJan Ekström <jeebjp@gmail.com>2018-04-29 02:21:32 +0300
commit6c8362ef54f4e90476553cb6b64996cc414da06d (patch)
treeab3fa75a18958bacc08c473304f59519a58f5969
parent8135e25600ace2894df274e6a825cfef525fee77 (diff)
downloadmpv-6c8362ef54f4e90476553cb6b64996cc414da06d.tar.bz2
mpv-6c8362ef54f4e90476553cb6b64996cc414da06d.tar.xz
encode: rewrite half of it
The main change is that we wait with opening the muxer ("writing headers") until we have data from all streams. This fixes race conditions at init due to broken assumptions in the old code. This also changes a lot of other stuff. I found and fixed a few API violations (often things for which better mechanisms were invented, and the old ones are not valid anymore). I try to get away from the public mutex and shared fields in encode_lavc_context. For now it's still needed for some timestamp-related fields, but most are gone. It also removes some bad code duplication between audio and video paths.
-rw-r--r--DOCS/man/encode.rst15
-rw-r--r--audio/out/ao_lavc.c240
-rw-r--r--common/common.h11
-rw-r--r--common/encode.h11
-rw-r--r--common/encode_lavc.c1136
-rw-r--r--common/encode_lavc.h105
-rw-r--r--etc/encoding-profiles.conf6
-rw-r--r--player/loadfile.c18
-rw-r--r--player/main.c7
-rw-r--r--player/video.c7
-rw-r--r--video/out/vo_lavc.c312
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));)
- {