diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/av_common.c | 84 | ||||
-rw-r--r-- | common/av_common.h | 9 | ||||
-rw-r--r-- | common/av_log.c | 62 | ||||
-rw-r--r-- | common/common.c | 59 | ||||
-rw-r--r-- | common/common.h | 34 | ||||
-rw-r--r-- | common/encode.h | 13 | ||||
-rw-r--r-- | common/encode_lavc.c | 186 | ||||
-rw-r--r-- | common/encode_lavc.h | 18 | ||||
-rw-r--r-- | common/meson.build | 11 | ||||
-rw-r--r-- | common/msg.c | 633 | ||||
-rw-r--r-- | common/msg_control.h | 4 | ||||
-rw-r--r-- | common/playlist.c | 118 | ||||
-rw-r--r-- | common/playlist.h | 31 | ||||
-rw-r--r-- | common/recorder.c | 108 | ||||
-rw-r--r-- | common/recorder.h | 5 | ||||
-rw-r--r-- | common/stats.c | 87 | ||||
-rw-r--r-- | common/stats.h | 2 | ||||
-rw-r--r-- | common/tags.c | 7 | ||||
-rw-r--r-- | common/tags.h | 2 | ||||
-rw-r--r-- | common/version.c | 6 | ||||
-rw-r--r-- | common/version.h.in | 7 |
21 files changed, 965 insertions, 521 deletions
diff --git a/common/av_common.c b/common/av_common.c index 01428ff2f4..277601d2fc 100644 --- a/common/av_common.c +++ b/common/av_common.c @@ -30,6 +30,7 @@ #include "config.h" +#include "audio/chmap_avchannel.h" #include "common/common.h" #include "common/msg.h" #include "demux/packet.h" @@ -39,20 +40,6 @@ #include "av_common.h" #include "codecs.h" -int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size) -{ - if (size) { - av_free(avctx->extradata); - avctx->extradata_size = 0; - avctx->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!avctx->extradata) - return -1; - avctx->extradata_size = size; - memcpy(avctx->extradata, ptr, size); - } - return 0; -} - enum AVMediaType mp_to_av_stream_type(int type) { switch (type) { @@ -63,7 +50,7 @@ enum AVMediaType mp_to_av_stream_type(int type) } } -AVCodecParameters *mp_codec_params_to_av(struct mp_codec_params *c) +AVCodecParameters *mp_codec_params_to_av(const struct mp_codec_params *c) { AVCodecParameters *avp = avcodec_parameters_alloc(); if (!avp) @@ -80,12 +67,23 @@ AVCodecParameters *mp_codec_params_to_av(struct mp_codec_params *c) avp->codec_id = mp_codec_to_av_codec_id(c->codec); avp->codec_tag = c->codec_tag; if (c->extradata_size) { - avp->extradata = - av_mallocz(c->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); + uint8_t *extradata = c->extradata; + int size = c->extradata_size; + + if (avp->codec_id == AV_CODEC_ID_FLAC) { + // ffmpeg expects FLAC extradata to be just the STREAMINFO, + // so grab only that (and assume it'll be the first block) + if (size >= 8 && !memcmp(c->extradata, "fLaC", 4)) { + extradata += 8; + size = MPMIN(34, size - 8); // FLAC_STREAMINFO_SIZE + } + } + + avp->extradata = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE); if (!avp->extradata) goto error; - avp->extradata_size = c->extradata_size; - memcpy(avp->extradata, c->extradata, avp->extradata_size); + avp->extradata_size = size; + memcpy(avp->extradata, extradata, size); } avp->bits_per_coded_sample = c->bits_per_coded_sample; @@ -97,9 +95,14 @@ AVCodecParameters *mp_codec_params_to_av(struct mp_codec_params *c) avp->sample_rate = c->samplerate; avp->bit_rate = c->bitrate; avp->block_align = c->block_align; + +#if !HAVE_AV_CHANNEL_LAYOUT avp->channels = c->channels.num; if (!mp_chmap_is_unknown(&c->channels)) avp->channel_layout = mp_chmap_to_lavc(&c->channels); +#else + mp_chmap_to_av_layout(&avp->ch_layout, &c->channels); +#endif return avp; error: @@ -108,7 +111,7 @@ error: } // Set avctx codec headers for decoding. Returns <0 on failure. -int mp_set_avctx_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c) +int mp_set_avctx_codec_headers(AVCodecContext *avctx, const struct mp_codec_params *c) { enum AVMediaType codec_type = avctx->codec_type; enum AVCodecID codec_id = avctx->codec_id; @@ -128,7 +131,7 @@ int mp_set_avctx_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c) // Pick a "good" timebase, which will be used to convert double timestamps // back to fractions for passing them through libavcodec. -AVRational mp_get_codec_timebase(struct mp_codec_params *c) +AVRational mp_get_codec_timebase(const struct mp_codec_params *c) { AVRational tb = {c->native_tb_num, c->native_tb_den}; if (tb.num < 1 || tb.den < 1) { @@ -179,7 +182,11 @@ double mp_pts_from_av(int64_t av_pts, AVRational *tb) // Set duration field only if tb is set. void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb) { - av_init_packet(dst); + dst->side_data = NULL; + dst->side_data_elems = 0; + dst->buf = NULL; + av_packet_unref(dst); + dst->data = mpkt ? mpkt->buffer : NULL; dst->size = mpkt ? mpkt->len : 0; /* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info @@ -270,7 +277,7 @@ int mp_codec_to_av_codec_id(const char *codec) if (desc) id = desc->id; if (id == AV_CODEC_ID_NONE) { - AVCodec *avcodec = avcodec_find_decoder_by_name(codec); + const AVCodec *avcodec = avcodec_find_decoder_by_name(codec); if (avcodec) id = avcodec->id; } @@ -285,7 +292,7 @@ const char *mp_codec_from_av_codec_id(int codec_id) if (desc) name = desc->name; if (!name) { - AVCodec *avcodec = avcodec_find_decoder(codec_id); + const AVCodec *avcodec = avcodec_find_decoder(codec_id); if (avcodec) name = avcodec->name; } @@ -377,3 +384,32 @@ int mp_set_avopts_pos(struct mp_log *log, void *avobj, void *posargs, char **kv) } return success; } + +/** + * Must be used to free an AVPacket that was used with mp_set_av_packet(). + * + * We have a particular pattern where we "borrow" buffers and set them + * into an AVPacket to pass data to ffmpeg without extra copies. + * This applies to buf and side_data, so this function clears them before + * freeing. + */ +void mp_free_av_packet(AVPacket **pkt) +{ + if (*pkt) { + (*pkt)->side_data = NULL; + (*pkt)->side_data_elems = 0; + (*pkt)->buf = NULL; + } + av_packet_free(pkt); +} + +void mp_codec_info_from_av(const AVCodecContext *avctx, struct mp_codec_params *c) +{ + c->codec_profile = av_get_profile_name(avctx->codec, avctx->profile); + if (!c->codec_profile) + c->codec_profile = avcodec_profile_name(avctx->codec_id, avctx->profile); + c->codec = avctx->codec_descriptor->name; + c->codec_desc = avctx->codec_descriptor->long_name; + c->decoder = avctx->codec->name; + c->decoder_desc = avctx->codec->long_name; +} diff --git a/common/av_common.h b/common/av_common.h index 1b3e468884..c584085890 100644 --- a/common/av_common.h +++ b/common/av_common.h @@ -31,11 +31,10 @@ struct mp_codec_params; struct AVDictionary; struct mp_log; -int mp_lavc_set_extradata(AVCodecContext *avctx, void *ptr, int size); enum AVMediaType mp_to_av_stream_type(int type); -AVCodecParameters *mp_codec_params_to_av(struct mp_codec_params *c); -int mp_set_avctx_codec_headers(AVCodecContext *avctx, struct mp_codec_params *c); -AVRational mp_get_codec_timebase(struct mp_codec_params *c); +AVCodecParameters *mp_codec_params_to_av(const struct mp_codec_params *c); +int mp_set_avctx_codec_headers(AVCodecContext *avctx, const struct mp_codec_params *c); +AVRational mp_get_codec_timebase(const struct mp_codec_params *c); void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb); int64_t mp_pts_to_av(double mp_pts, AVRational *tb); double mp_pts_from_av(int64_t av_pts, AVRational *tb); @@ -50,5 +49,7 @@ void mp_set_avdict(struct AVDictionary **dict, char **kv); void mp_avdict_print_unset(struct mp_log *log, int msgl, struct AVDictionary *d); int mp_set_avopts(struct mp_log *log, void *avobj, char **kv); int mp_set_avopts_pos(struct mp_log *log, void *avobj, void *posargs, char **kv); +void mp_free_av_packet(AVPacket **pkt); +void mp_codec_info_from_av(const AVCodecContext *avctx, struct mp_codec_params *c); #endif diff --git a/common/av_log.c b/common/av_log.c index 8122d3a65b..54b78a617d 100644 --- a/common/av_log.c +++ b/common/av_log.c @@ -22,22 +22,29 @@ #include <stdlib.h> #include <stdio.h> #include <stdbool.h> -#include <pthread.h> #include "av_log.h" -#include "config.h" #include "common/common.h" #include "common/global.h" #include "common/msg.h" +#include "config.h" +#include "misc/bstr.h" +#include "osdep/threads.h" #include <libavutil/avutil.h> #include <libavutil/log.h> +#include <libavutil/version.h> #include <libavcodec/avcodec.h> +#include <libavcodec/version.h> #include <libavformat/avformat.h> +#include <libavformat/version.h> #include <libswresample/swresample.h> +#include <libswresample/version.h> #include <libswscale/swscale.h> +#include <libswscale/version.h> #include <libavfilter/avfilter.h> +#include <libavfilter/version.h> #if HAVE_LIBAVDEVICE #include <libavdevice/avdevice.h> @@ -45,10 +52,11 @@ // Needed because the av_log callback does not provide a library-safe message // callback. -static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER; +static mp_static_mutex log_lock = MP_STATIC_MUTEX_INITIALIZER; static struct mpv_global *log_mpv_instance; static struct mp_log *log_root, *log_decaudio, *log_decvideo, *log_demuxer; static bool log_print_prefix = true; +static bstr log_buffer; static int av_log_level_to_mp_level(int av_level) { @@ -74,7 +82,7 @@ static struct mp_log *get_av_log(void *ptr) if (!avc) { mp_warn(log_root, "av_log callback called with bad parameters (NULL AVClass).\n" - "This is a bug in one of Libav/FFmpeg libraries used.\n"); + "This is a bug in one of FFmpeg libraries used.\n"); return log_root; } @@ -100,6 +108,11 @@ static struct mp_log *get_av_log(void *ptr) return log_root; } +static const char *avclass_item_name(void *obj, const AVClass *avc) +{ + return (avc->item_name ? avc->item_name : av_default_item_name)(obj); +} + static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt, va_list vl) { @@ -107,10 +120,10 @@ static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt, int mp_level = av_log_level_to_mp_level(level); // Note: mp_log is thread-safe, but destruction of the log instances is not. - pthread_mutex_lock(&log_lock); + mp_mutex_lock(&log_lock); if (!log_mpv_instance) { - pthread_mutex_unlock(&log_lock); + mp_mutex_unlock(&log_lock); // Fallback to stderr vfprintf(stderr, fmt, vl); return; @@ -119,34 +132,36 @@ static void mp_msg_av_log_callback(void *ptr, int level, const char *fmt, struct mp_log *log = get_av_log(ptr); if (mp_msg_test(log, mp_level)) { - char buffer[4096] = ""; - int pos = 0; - const char *prefix = avc ? avc->item_name(ptr) : NULL; - if (log_print_prefix && prefix) - pos = snprintf(buffer, sizeof(buffer), "%s: ", prefix); - log_print_prefix = fmt[strlen(fmt) - 1] == '\n'; - - pos = MPMIN(MPMAX(pos, 0), sizeof(buffer)); - vsnprintf(buffer + pos, sizeof(buffer) - pos, fmt, vl); - - mp_msg(log, mp_level, "%s", buffer); + log_buffer.len = 0; + bstr_xappend_vasprintf(log_root, &log_buffer, fmt, vl); + if (!log_buffer.len) + goto done; + const char *prefix = avc ? avclass_item_name(ptr, avc) : NULL; + if (log_print_prefix && prefix) { + mp_msg(log, mp_level, "%s: %.*s", prefix, BSTR_P(log_buffer)); + } else { + mp_msg(log, mp_level, "%.*s", BSTR_P(log_buffer)); + } + log_print_prefix = log_buffer.start[log_buffer.len - 1] == '\n'; } - pthread_mutex_unlock(&log_lock); +done: + mp_mutex_unlock(&log_lock); } void init_libav(struct mpv_global *global) { - pthread_mutex_lock(&log_lock); + mp_mutex_lock(&log_lock); if (!log_mpv_instance) { log_mpv_instance = global; log_root = mp_log_new(NULL, global->log, "ffmpeg"); log_decaudio = mp_log_new(log_root, log_root, "audio"); log_decvideo = mp_log_new(log_root, log_root, "video"); log_demuxer = mp_log_new(log_root, log_root, "demuxer"); + log_buffer = (bstr){0}; av_log_set_callback(mp_msg_av_log_callback); } - pthread_mutex_unlock(&log_lock); + mp_mutex_unlock(&log_lock); avformat_network_init(); @@ -157,13 +172,13 @@ void init_libav(struct mpv_global *global) void uninit_libav(struct mpv_global *global) { - pthread_mutex_lock(&log_lock); + mp_mutex_lock(&log_lock); if (log_mpv_instance == global) { av_log_set_callback(av_log_default_callback); log_mpv_instance = NULL; talloc_free(log_root); } - pthread_mutex_unlock(&log_lock); + mp_mutex_unlock(&log_lock); } #define V(x) AV_VERSION_MAJOR(x), \ @@ -187,6 +202,7 @@ void check_library_versions(struct mp_log *log, int v) {"libswresample", LIBSWRESAMPLE_VERSION_INT, swresample_version()}, }; + mp_msg(log, v, "FFmpeg version: %s\n", av_version_info()); mp_msg(log, v, "FFmpeg library versions:\n"); for (int n = 0; n < MP_ARRAY_SIZE(libs); n++) { @@ -203,8 +219,6 @@ void check_library_versions(struct mp_log *log, int v) abort(); } } - - mp_msg(log, v, "FFmpeg version: %s\n", av_version_info()); } #undef V diff --git a/common/common.c b/common/common.c index 6607f95c3c..7089a6674b 100644 --- a/common/common.c +++ b/common/common.c @@ -21,6 +21,7 @@ #include <libavutil/common.h> #include <libavutil/error.h> +#include <libavutil/mathematics.h> #include "mpv_talloc.h" #include "misc/bstr.h" @@ -93,6 +94,25 @@ char *mp_format_time(double time, bool fractions) return mp_format_time_fmt(fractions ? "%H:%M:%S.%T" : "%H:%M:%S", time); } +char *mp_format_double(void *talloc_ctx, double val, int precision, + bool plus_sign, bool percent_sign, bool trim) +{ + bstr str = {0}; + const char *fmt = plus_sign ? "%+.*f" : "%.*f"; + bstr_xappend_asprintf(talloc_ctx, &str, fmt, precision, val); + size_t pos = str.len; + if (trim) { + while (--pos && str.start[pos] == '0') + str.len--; + if (str.start[pos] == '.') + str.len--; + } + if (percent_sign) + bstr_xappend(talloc_ctx, &str, bstr0("%")); + str.start[str.len] = '\0'; + return str.start; +} + // Set rc to the union of rc and rc2 void mp_rect_union(struct mp_rect *rc, const struct mp_rect *rc2) { @@ -120,12 +140,43 @@ bool mp_rect_intersection(struct mp_rect *rc, const struct mp_rect *rc2) return rc->x1 > rc->x0 && rc->y1 > rc->y0; } -bool mp_rect_equals(struct mp_rect *rc1, struct mp_rect *rc2) +bool mp_rect_equals(const struct mp_rect *rc1, const struct mp_rect *rc2) { return rc1->x0 == rc2->x0 && rc1->y0 == rc2->y0 && rc1->x1 == rc2->x1 && rc1->y1 == rc2->y1; } +// Rotate mp_rect by 90 degrees increments +void mp_rect_rotate(struct mp_rect *rc, int w, int h, int rotation) +{ + rotation %= 360; + + if (rotation >= 180) { + rotation -= 180; + MPSWAP(int, rc->x0, rc->x1); + MPSWAP(int, rc->y0, rc->y1); + } + + if (rotation == 90) { + *rc = (struct mp_rect) { + .x0 = rc->y1, + .y0 = rc->x0, + .x1 = rc->y0, + .y1 = rc->x1, + }; + } + + if (rc->x1 < rc->x0) { + rc->x0 = w - rc->x0; + rc->x1 = w - rc->x1; + } + + if (rc->y1 < rc->y0) { + rc->y0 = h - rc->y0; + rc->y1 = h - rc->y1; + } +} + // Compute rc1-rc2, put result in res_array, return number of rectangles in // res_array. In the worst case, there are 4 rectangles, so res_array must // provide that much storage space. @@ -373,3 +424,9 @@ uint32_t mp_round_next_power_of_2(uint32_t v) int l = mp_log2(v) + 1; return l == 32 ? 0 : (uint32_t)1 << l; } + +int mp_lcm(int x, int y) +{ + assert(x && y); + return x * (y / av_gcd(x, y)); +} diff --git a/common/common.h b/common/common.h index af01e38e10..cd9bea93c2 100644 --- a/common/common.h +++ b/common/common.h @@ -39,6 +39,7 @@ #define MPSWAP(type, a, b) \ do { type SWAP_tmp = b; b = a; a = SWAP_tmp; } while (0) #define MP_ARRAY_SIZE(s) (sizeof(s) / sizeof((s)[0])) +#define MP_DIV_UP(x, y) (((x) + (y) - 1) / (y)) // align must be a power of two (align >= 1), x >= 0 #define MP_ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1)) @@ -46,6 +47,9 @@ #define MP_IS_ALIGNED(x, align) (!((x) & ((align) - 1))) #define MP_IS_POWER_OF_2(x) ((x) > 0 && !((x) & ((x) - 1))) +// align to non power of two +#define MP_ALIGN_NPOT(x, align) ((align) ? MP_DIV_UP(x, align) * (align) : (x)) + // Return "a", or if that is NOPTS, return "def". #define MP_PTS_OR_DEF(a, def) ((a) == MP_NOPTS_VALUE ? (def) : (a)) // If one of the values is NOPTS, always pick the other one. @@ -68,6 +72,26 @@ enum stream_type { STREAM_TYPE_COUNT, }; +enum video_sync { + VS_DEFAULT = 0, + VS_DISP_RESAMPLE, + VS_DISP_RESAMPLE_VDROP, + VS_DISP_RESAMPLE_NONE, + VS_DISP_TEMPO, + VS_DISP_ADROP, + VS_DISP_VDROP, + VS_DISP_NONE, + VS_NONE, +}; + +#define VS_IS_DISP(x) ((x) == VS_DISP_RESAMPLE || \ + (x) == VS_DISP_RESAMPLE_VDROP || \ + (x) == VS_DISP_RESAMPLE_NONE || \ + (x) == VS_DISP_TEMPO || \ + (x) == VS_DISP_ADROP || \ + (x) == VS_DISP_VDROP || \ + (x) == VS_DISP_NONE) + extern const char mpv_version[]; extern const char mpv_builddate[]; extern const char mpv_copyright[]; @@ -75,6 +99,12 @@ extern const char mpv_copyright[]; char *mp_format_time(double time, bool fractions); char *mp_format_time_fmt(const char *fmt, double time); +// Formats a double value to a string with the specified precision. +// Trailing zeros (and the dot) can be trimmed. +// Optionally, a plus sign and a percent sign can be added. +char *mp_format_double(void *talloc_ctx, double val, int precision, + bool plus_sign, bool percent_sign, bool trim); + struct mp_rect { int x0, y0; int x1, y1; @@ -86,12 +116,14 @@ struct mp_rect { void mp_rect_union(struct mp_rect *rc, const struct mp_rect *src); bool mp_rect_intersection(struct mp_rect *rc, const struct mp_rect *rc2); bool mp_rect_contains(struct mp_rect *rc, int x, int y); -bool mp_rect_equals(struct mp_rect *rc1, struct mp_rect *rc2); +bool mp_rect_equals(const struct mp_rect *rc1, const struct mp_rect *rc2); int mp_rect_subtract(const struct mp_rect *rc1, const struct mp_rect *rc2, struct mp_rect res_array[4]); +void mp_rect_rotate(struct mp_rect *rc, int w, int h, int rotation); unsigned int mp_log2(uint32_t v); uint32_t mp_round_next_power_of_2(uint32_t v); +int mp_lcm(int x, int y); int mp_snprintf_cat(char *str, size_t size, const char *format, ...) PRINTF_ATTRIBUTE(3, 4); diff --git a/common/encode.h b/common/encode.h index 62a8b094e9..33d778cd67 100644 --- a/common/encode.h +++ b/common/encode.h @@ -38,12 +38,8 @@ struct encode_opts { char **vopts; char *acodec; char **aopts; - float voffset; - float aoffset; - int rawts; - int video_first; - int audio_first; - int copy_metadata; + bool rawts; + bool copy_metadata; char **set_metadata; char **remove_metadata; }; @@ -54,13 +50,12 @@ 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); +bool encode_lavc_stream_type_ok(struct encode_lavc_context *ctx, + enum stream_type type); void encode_lavc_expect_stream(struct encode_lavc_context *ctx, enum stream_type type); -void encode_lavc_stream_eof(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_audio_pts(struct encode_lavc_context *ctx, double pts); bool encode_lavc_didfail(struct encode_lavc_context *ctx); // check if encoding failed #endif diff --git a/common/encode_lavc.c b/common/encode_lavc.c index 94428c6733..a250f9c994 100644 --- a/common/encode_lavc.c +++ b/common/encode_lavc.c @@ -23,7 +23,6 @@ #include <libavutil/avutil.h> #include <libavutil/timestamp.h> -#include "config.h" #include "encode_lavc.h" #include "common/av_common.h" #include "common/global.h" @@ -77,38 +76,22 @@ struct mux_stream { #define OPT_BASE_STRUCT struct encode_opts const struct m_sub_options encode_config = { .opts = (const m_option_t[]) { - {"o", OPT_STRING(file), .flags = CONF_NOCFG | CONF_PRE_PARSE | M_OPT_FILE, - .deprecation_message = "lack of maintainer"}, + {"o", OPT_STRING(file), .flags = CONF_NOCFG | CONF_PRE_PARSE | M_OPT_FILE}, {"of", OPT_STRING(format)}, {"ofopts", OPT_KEYVALUELIST(fopts), .flags = M_OPT_HAVE_HELP}, {"ovc", OPT_STRING(vcodec)}, {"ovcopts", OPT_KEYVALUELIST(vopts), .flags = M_OPT_HAVE_HELP}, {"oac", OPT_STRING(acodec)}, {"oacopts", OPT_KEYVALUELIST(aopts), .flags = M_OPT_HAVE_HELP}, - {"ovoffset", OPT_FLOAT(voffset), M_RANGE(-1000000.0, 1000000.0), - .deprecation_message = "--audio-delay (once unbroken)"}, - {"oaoffset", OPT_FLOAT(aoffset), M_RANGE(-1000000.0, 1000000.0), - .deprecation_message = "--audio-delay (once unbroken)"}, - {"orawts", OPT_FLAG(rawts)}, - {"ovfirst", OPT_FLAG(video_first), - .deprecation_message = "no replacement"}, - {"oafirst", OPT_FLAG(audio_first), - .deprecation_message = "no replacement"}, - {"ocopy-metadata", OPT_FLAG(copy_metadata)}, + {"orawts", OPT_BOOL(rawts)}, + {"ocopy-metadata", OPT_BOOL(copy_metadata)}, {"oset-metadata", OPT_KEYVALUELIST(set_metadata)}, {"oremove-metadata", OPT_STRINGLIST(remove_metadata)}, - - {"ocopyts", OPT_REMOVED("ocopyts is now the default")}, - {"oneverdrop", OPT_REMOVED("no replacement")}, - {"oharddup", OPT_REMOVED("use --vf-add=fps=VALUE")}, - {"ofps", OPT_REMOVED("no replacement (use --vf-add=fps=VALUE for CFR)")}, - {"oautofps", OPT_REMOVED("no replacement")}, - {"omaxfps", OPT_REMOVED("no replacement")}, {0} }, .size = sizeof(struct encode_opts), .defaults = &(const struct encode_opts){ - .copy_metadata = 1, + .copy_metadata = true, }, }; @@ -121,7 +104,7 @@ struct encode_lavc_context *encode_lavc_init(struct mpv_global *global) .priv = talloc_zero(ctx, struct encode_priv), .log = mp_log_new(ctx, global->log, "encode"), }; - pthread_mutex_init(&ctx->lock, NULL); + mp_mutex_init(&ctx->lock); struct encode_priv *p = ctx->priv; p->log = ctx->log; @@ -174,7 +157,7 @@ void encode_lavc_set_metadata(struct encode_lavc_context *ctx, { struct encode_priv *p = ctx->priv; - pthread_mutex_lock(&ctx->lock); + mp_mutex_lock(&ctx->lock); if (ctx->options->copy_metadata) { p->metadata = mp_tags_dup(ctx, metadata); @@ -201,7 +184,7 @@ void encode_lavc_set_metadata(struct encode_lavc_context *ctx, } } - pthread_mutex_unlock(&ctx->lock); + mp_mutex_unlock(&ctx->lock); } bool encode_lavc_free(struct encode_lavc_context *ctx) @@ -237,22 +220,12 @@ bool encode_lavc_free(struct encode_lavc_context *ctx) res = !p->failed; - pthread_mutex_destroy(&ctx->lock); + mp_mutex_destroy(&ctx->lock); talloc_free(ctx); return res; } -void encode_lavc_set_audio_pts(struct encode_lavc_context *ctx, double pts) -{ - if (ctx) { - pthread_mutex_lock(&ctx->lock); - ctx->last_audio_in_pts = pts; - ctx->samples_since_last_pts = 0; - pthread_mutex_unlock(&ctx->lock); - } -} - // called locked static void maybe_init_muxer(struct encode_lavc_context *ctx) { @@ -340,7 +313,7 @@ void encode_lavc_expect_stream(struct encode_lavc_context *ctx, { struct encode_priv *p = ctx->priv; - pthread_mutex_lock(&ctx->lock); + mp_mutex_lock(&ctx->lock); enum AVMediaType codec_type = mp_to_av_stream_type(type); @@ -364,32 +337,7 @@ void encode_lavc_expect_stream(struct encode_lavc_context *ctx, MP_TARRAY_APPEND(p, p->streams, p->num_streams, dst); done: - pthread_mutex_unlock(&ctx->lock); -} - -void encode_lavc_stream_eof(struct encode_lavc_context *ctx, - enum stream_type type) -{ - if (!ctx) - return; - - struct encode_priv *p = ctx->priv; - - pthread_mutex_lock(&ctx->lock); - - enum AVMediaType codec_type = mp_to_av_stream_type(type); - struct mux_stream *dst = find_mux_stream(ctx, codec_type); - - // If we've reached EOF, even though the stream was selected, and we didn't - // ever initialize it, we have a problem. We could mux some sort of dummy - // stream (and could fail if actual data arrives later), or we bail out - // early. - if (dst && !dst->st) { - MP_ERR(p, "No data on stream %s.\n", dst->name); - p->failed = true; - } - - pthread_mutex_unlock(&ctx->lock); + mp_mutex_unlock(&ctx->lock); } // Signal that you are ready to encode (you provide the codec params etc. too). @@ -403,7 +351,7 @@ static void encode_lavc_add_stream(struct encoder_context *enc, { struct encode_priv *p = ctx->priv; - pthread_mutex_lock(&ctx->lock); + mp_mutex_lock(&ctx->lock); struct mux_stream *dst = find_mux_stream(ctx, info->codecpar->codec_type); if (!dst) { @@ -439,7 +387,7 @@ static void encode_lavc_add_stream(struct encoder_context *enc, maybe_init_muxer(ctx); done: - pthread_mutex_unlock(&ctx->lock); + mp_mutex_unlock(&ctx->lock); } // Write a packet. This will take over ownership of `pkt` @@ -450,7 +398,7 @@ static void encode_lavc_add_packet(struct mux_stream *dst, AVPacket *pkt) assert(dst->st); - pthread_mutex_lock(&ctx->lock); + mp_mutex_lock(&ctx->lock); if (p->failed) goto done; @@ -487,7 +435,7 @@ static void encode_lavc_add_packet(struct mux_stream *dst, AVPacket *pkt) pkt = NULL; done: - pthread_mutex_unlock(&ctx->lock); + mp_mutex_unlock(&ctx->lock); if (pkt) av_packet_unref(pkt); } @@ -502,12 +450,9 @@ void encode_lavc_discontinuity(struct encode_lavc_context *ctx) if (!ctx) return; - pthread_mutex_lock(&ctx->lock); - - ctx->audio_pts_offset = MP_NOPTS_VALUE; + mp_mutex_lock(&ctx->lock); ctx->discontinuity_pts_offset = MP_NOPTS_VALUE; - - pthread_mutex_unlock(&ctx->lock); + mp_mutex_unlock(&ctx->lock); } static void encode_lavc_printoptions(struct mp_log *log, const void *obj, @@ -728,7 +673,7 @@ int encode_lavc_getstatus(struct encode_lavc_context *ctx, float minutes, megabytes, fps, x; float f = MPMAX(0.0001, relative_position); - pthread_mutex_lock(&ctx->lock); + mp_mutex_lock(&ctx->lock); if (p->failed) { snprintf(buf, bufsize, "(failed)\n"); @@ -752,7 +697,7 @@ int encode_lavc_getstatus(struct encode_lavc_context *ctx, buf[bufsize - 1] = 0; done: - pthread_mutex_unlock(&ctx->lock); + mp_mutex_unlock(&ctx->lock); return 0; } @@ -760,9 +705,9 @@ bool encode_lavc_didfail(struct encode_lavc_context *ctx) { if (!ctx) return false; - pthread_mutex_lock(&ctx->lock); + mp_mutex_lock(&ctx->lock); bool fail = ctx->priv->failed; - pthread_mutex_unlock(&ctx->lock); + mp_mutex_unlock(&ctx->lock); return fail; } @@ -770,10 +715,53 @@ static void encoder_destroy(void *ptr) { struct encoder_context *p = ptr; + av_packet_free(&p->pkt); + avcodec_parameters_free(&p->info.codecpar); avcodec_free_context(&p->encoder); free_stream(p->twopass_bytebuffer); } +static const AVCodec *find_codec_for(struct encode_lavc_context *ctx, + enum stream_type type, bool *used_auto) +{ + char *codec_name = type == STREAM_VIDEO + ? ctx->options->vcodec + : ctx->options->acodec; + enum AVMediaType codec_type = mp_to_av_stream_type(type); + const char *tname = stream_type_name(type); + + *used_auto = !(codec_name && codec_name[0]); + + const AVCodec *codec; + if (*used_auto) { + codec = avcodec_find_encoder(av_guess_codec(ctx->oformat, NULL, + ctx->options->file, NULL, + codec_type)); + } else { + codec = avcodec_find_encoder_by_name(codec_name); + if (!codec) + MP_FATAL(ctx, "codec '%s' not found.\n", codec_name); + } + + if (codec && codec->type != codec_type) { + MP_FATAL(ctx, "codec for %s has wrong media type\n", tname); + codec = NULL; + } + + return codec; +} + +// Return whether the stream type is "supposed" to work. +bool encode_lavc_stream_type_ok(struct encode_lavc_context *ctx, + enum stream_type type) +{ + // If a codec was forced, let it proceed to actual encoding, and then error + // if it doesn't work. (Worried that av_guess_codec() may return NULL for + // some formats where a specific codec works anyway.) + bool auto_codec; + return !!find_codec_for(ctx, type, &auto_codec) || !auto_codec; +} + struct encoder_context *encoder_context_alloc(struct encode_lavc_context *ctx, enum stream_type type, struct mp_log *log) @@ -794,27 +782,13 @@ struct encoder_context *encoder_context_alloc(struct encode_lavc_context *ctx, .encode_lavc_ctx = ctx, }; - char *codec_name = type == STREAM_VIDEO - ? p->options->vcodec - : p->options->acodec; - enum AVMediaType codec_type = mp_to_av_stream_type(type); + bool auto_codec; + const AVCodec *codec = find_codec_for(ctx, type, &auto_codec); const char *tname = stream_type_name(type); - AVCodec *codec; - if (codec_name&& codec_name[0]) { - codec = avcodec_find_encoder_by_name(codec_name); - } else { - codec = avcodec_find_encoder(av_g |