diff options
Diffstat (limited to 'filters')
-rw-r--r-- | filters/f_async_queue.c | 66 | ||||
-rw-r--r-- | filters/f_async_queue.h | 1 | ||||
-rw-r--r-- | filters/f_auto_filters.c | 69 | ||||
-rw-r--r-- | filters/f_auto_filters.h | 4 | ||||
-rw-r--r-- | filters/f_autoconvert.c | 111 | ||||
-rw-r--r-- | filters/f_decoder_wrapper.c | 210 | ||||
-rw-r--r-- | filters/f_decoder_wrapper.h | 6 | ||||
-rw-r--r-- | filters/f_demux_in.c | 12 | ||||
-rw-r--r-- | filters/f_hwtransfer.c | 499 | ||||
-rw-r--r-- | filters/f_hwtransfer.h | 17 | ||||
-rw-r--r-- | filters/f_lavfi.c | 108 | ||||
-rw-r--r-- | filters/f_lavfi.h | 3 | ||||
-rw-r--r-- | filters/f_output_chain.c | 73 | ||||
-rw-r--r-- | filters/f_output_chain.h | 3 | ||||
-rw-r--r-- | filters/f_swresample.c | 62 | ||||
-rw-r--r-- | filters/f_swresample.h | 4 | ||||
-rw-r--r-- | filters/f_swscale.c | 10 | ||||
-rw-r--r-- | filters/f_utils.c | 2 | ||||
-rw-r--r-- | filters/f_utils.h | 6 | ||||
-rw-r--r-- | filters/filter.c | 47 | ||||
-rw-r--r-- | filters/filter.h | 7 | ||||
-rw-r--r-- | filters/filter_internal.h | 2 | ||||
-rw-r--r-- | filters/user_filters.c | 20 |
23 files changed, 881 insertions, 461 deletions
diff --git a/filters/f_async_queue.c b/filters/f_async_queue.c index 5ee32be5fa..95db385d7f 100644 --- a/filters/f_async_queue.c +++ b/filters/f_async_queue.c @@ -1,10 +1,10 @@ #include <limits.h> -#include <pthread.h> +#include <stdatomic.h> #include "audio/aframe.h" #include "common/common.h" #include "common/msg.h" -#include "osdep/atomic.h" +#include "osdep/threads.h" #include "f_async_queue.h" #include "filter_internal.h" @@ -16,9 +16,9 @@ struct mp_async_queue { }; struct async_queue { - mp_atomic_uint64 refcount; + _Atomic uint64_t refcount; - pthread_mutex_t lock; + mp_mutex lock; // -- protected by lock struct mp_async_queue_config cfg; @@ -34,7 +34,7 @@ struct async_queue { static void reset_queue(struct async_queue *q) { - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); q->active = q->reading = false; for (int n = 0; n < q->num_frames; n++) mp_frame_unref(&q->frames[n]); @@ -46,7 +46,7 @@ static void reset_queue(struct async_queue *q) if (q->conn[n]) mp_filter_wakeup(q->conn[n]); } - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); } static void unref_queue(struct async_queue *q) @@ -57,7 +57,7 @@ static void unref_queue(struct async_queue *q) assert(count >= 0); if (count == 0) { reset_queue(q); - pthread_mutex_destroy(&q->lock); + mp_mutex_destroy(&q->lock); talloc_free(q); } } @@ -73,9 +73,9 @@ struct mp_async_queue *mp_async_queue_create(void) struct mp_async_queue *r = talloc_zero(NULL, struct mp_async_queue); r->q = talloc_zero(NULL, struct async_queue); *r->q = (struct async_queue){ - .refcount = ATOMIC_VAR_INIT(1), + .refcount = 1, }; - pthread_mutex_init(&r->q->lock, NULL); + mp_mutex_init(&r->q->lock); talloc_set_destructor(r, on_free_queue); mp_async_queue_set_config(r, (struct mp_async_queue_config){0}); return r; @@ -142,12 +142,12 @@ void mp_async_queue_set_config(struct mp_async_queue *queue, cfg.max_samples = MPMAX(cfg.max_samples, 1); - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); bool recompute = q->cfg.sample_unit != cfg.sample_unit; q->cfg = cfg; if (recompute) recompute_sizes(q); - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); } void mp_async_queue_reset(struct mp_async_queue *queue) @@ -158,18 +158,18 @@ void mp_async_queue_reset(struct mp_async_queue *queue) bool mp_async_queue_is_active(struct mp_async_queue *queue) { struct async_queue *q = queue->q; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); bool res = q->active; - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); return res; } bool mp_async_queue_is_full(struct mp_async_queue *queue) { struct async_queue *q = queue->q; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); bool res = is_full(q); - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); return res; } @@ -177,21 +177,21 @@ void mp_async_queue_resume(struct mp_async_queue *queue) { struct async_queue *q = queue->q; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); if (!q->active) { q->active = true; // Possibly make the consumer request new frames. if (q->conn[1]) mp_filter_wakeup(q->conn[1]); } - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); } void mp_async_queue_resume_reading(struct mp_async_queue *queue) { struct async_queue *q = queue->q; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); if (!q->active || !q->reading) { q->active = true; q->reading = true; @@ -201,24 +201,24 @@ void mp_async_queue_resume_reading(struct mp_async_queue *queue) mp_filter_wakeup(q->conn[n]); } } - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); } int64_t mp_async_queue_get_samples(struct mp_async_queue *queue) { struct async_queue *q = queue->q; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); int64_t res = q->samples_size; - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); return res; } int mp_async_queue_get_frames(struct mp_async_queue *queue) { struct async_queue *q = queue->q; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); int res = q->num_frames; - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); return res; } @@ -232,12 +232,12 @@ static void destroy(struct mp_filter *f) struct priv *p = f->priv; struct async_queue *q = p->q; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); for (int n = 0; n < 2; n++) { if (q->conn[n] == f) q->conn[n] = NULL; } - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); unref_queue(q); } @@ -248,7 +248,7 @@ static void process_in(struct mp_filter *f) struct async_queue *q = p->q; assert(q->conn[0] == f); - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); if (!q->reading) { // mp_async_queue_reset()/reset_queue() is usually called asynchronously, // so we might have requested a frame earlier, and now can't use it. @@ -274,7 +274,7 @@ static void process_in(struct mp_filter *f) } if (p->notify && !q->num_frames) mp_filter_wakeup(p->notify); - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); } static void process_out(struct mp_filter *f) @@ -286,7 +286,7 @@ static void process_out(struct mp_filter *f) if (!mp_pin_in_needs_data(f->ppins[0])) return; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); if (q->active && !q->reading) { q->reading = true; mp_filter_wakeup(q->conn[0]); @@ -301,7 +301,7 @@ static void process_out(struct mp_filter *f) if (q->conn[0]) mp_filter_wakeup(q->conn[0]); } - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); } static void reset(struct mp_filter *f) @@ -309,12 +309,12 @@ static void reset(struct mp_filter *f) struct priv *p = f->priv; struct async_queue *q = p->q; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); // If the queue is in reading state, it is logical that it should request // input immediately. if (mp_pin_get_dir(f->pins[0]) == MP_PIN_IN && q->reading) mp_filter_wakeup(f); - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); } // producer @@ -365,11 +365,11 @@ struct mp_filter *mp_async_queue_create_filter(struct mp_filter *parent, atomic_fetch_add(&q->refcount, 1); p->q = q; - pthread_mutex_lock(&q->lock); + mp_mutex_lock(&q->lock); int slot = is_in ? 0 : 1; assert(!q->conn[slot]); // fails if already connected on this end q->conn[slot] = f; - pthread_mutex_unlock(&q->lock); + mp_mutex_unlock(&q->lock); return f; } diff --git a/filters/f_async_queue.h b/filters/f_async_queue.h index e05b43a43b..46dafcdc97 100644 --- a/filters/f_async_queue.h +++ b/filters/f_async_queue.h @@ -1,5 +1,6 @@ #pragma once +#include <stdint.h> #include "filter.h" // A thread safe queue, which buffers a configurable number of frames like a diff --git a/filters/f_auto_filters.c b/filters/f_auto_filters.c index a5394dd35a..6fa38b96c2 100644 --- a/filters/f_auto_filters.c +++ b/filters/f_auto_filters.c @@ -6,6 +6,7 @@ #include "common/msg.h" #include "options/m_config.h" #include "options/options.h" +#include "video/filter/refqueue.h" #include "video/mp_image.h" #include "video/mp_image_pool.h" @@ -21,7 +22,7 @@ struct deint_priv { struct mp_subfilter sub; int prev_imgfmt; - int prev_setting; + bool deinterlace_active; struct m_config_cache *opts; }; @@ -45,15 +46,18 @@ static void deint_process(struct mp_filter *f) return; } + struct mp_image *img = frame.data; + bool interlaced = img->fields & MP_IMGFIELD_INTERLACED; + m_config_cache_update(p->opts); struct filter_opts *opts = p->opts->opts; + bool should_deinterlace = (opts->deinterlace == -1 && interlaced) || + opts->deinterlace == 1; - if (!opts->deinterlace) + if (!should_deinterlace) mp_subfilter_destroy(&p->sub); - struct mp_image *img = frame.data; - - if (img->imgfmt == p->prev_imgfmt && p->prev_setting == opts->deinterlace) { + if (img->imgfmt == p->prev_imgfmt && p->deinterlace_active == should_deinterlace) { mp_subfilter_continue(&p->sub); return; } @@ -64,24 +68,50 @@ static void deint_process(struct mp_filter *f) assert(!p->sub.filter); p->prev_imgfmt = img->imgfmt; - p->prev_setting = opts->deinterlace; - if (!p->prev_setting) { + p->deinterlace_active = should_deinterlace; + if (!p->deinterlace_active) { mp_subfilter_continue(&p->sub); return; } + char *field_parity; + switch (opts->field_parity) { + case MP_FIELD_PARITY_TFF: + field_parity = "tff"; + break; + case MP_FIELD_PARITY_BFF: + field_parity = "bff"; + break; + default: + field_parity = "auto"; + } + bool has_filter = true; if (img->imgfmt == IMGFMT_VDPAU) { - char *args[] = {"deint", "yes", NULL}; + char *args[] = {"deint", "yes", + "parity", field_parity, NULL}; p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "vdpaupp", args); } else if (img->imgfmt == IMGFMT_D3D11) { + char *args[] = {"parity", field_parity, NULL}; p->sub.filter = - mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "d3d11vpp", NULL); + mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "d3d11vpp", args); } else if (img->imgfmt == IMGFMT_CUDA) { - char *args[] = {"mode", "send_field", NULL}; + char *args[] = {"mode", "send_field", + "parity", field_parity, NULL}; p->sub.filter = - mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "yadif_cuda", args); + mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "bwdif_cuda", args); + } else if (img->imgfmt == IMGFMT_VULKAN) { + char *args[] = {"mode", "send_field", + "parity", field_parity, NULL}; + p->sub.filter = + mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "bwdif_vulkan", args); + } else if (img->imgfmt == IMGFMT_VAAPI) { + char *args[] = {"deint", "motion-adaptive", + "interlaced-only", "yes", + "parity", field_parity, NULL}; + p->sub.filter = + mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "vavpp", args); } else { has_filter = false; } @@ -96,7 +126,7 @@ static void deint_process(struct mp_filter *f) struct mp_autoconvert *ac = mp_autoconvert_create(subf); if (ac) { filters[0] = ac->f; - // We know vf_yadif does not support hw inputs. + // We know vf_bwdif does not support hw inputs. mp_autoconvert_add_all_sw_imgfmts(ac); if (!mp_autoconvert_probe_input_video(ac, img)) { @@ -108,9 +138,10 @@ static void deint_process(struct mp_filter *f) } } - char *args[] = {"mode", "send_field", NULL}; + char *args[] = {"mode", "send_field", + "parity", field_parity, NULL}; filters[1] = - mp_create_user_filter(subf, MP_OUTPUT_CHAIN_VIDEO, "yadif", args); + mp_create_user_filter(subf, MP_OUTPUT_CHAIN_VIDEO, "bwdif", args); mp_chain_filters(subf->ppins[0], subf->ppins[1], filters, 2); p->sub.filter = subf; @@ -154,6 +185,12 @@ static const struct mp_filter_info deint_filter = { .destroy = deint_destroy, }; +bool mp_deint_active(struct mp_filter *f) +{ + struct deint_priv *p = f->priv; + return p->deinterlace_active; +} + struct mp_filter *mp_deint_create(struct mp_filter *parent) { struct mp_filter *f = mp_filter_create(parent, &deint_filter); @@ -329,9 +366,9 @@ static void aspeed_process(struct mp_filter *f) if (req_filter) { if (req_filter == 1) { - MP_VERBOSE(f, "adding scaletempo\n"); + MP_VERBOSE(f, "adding scaletempo2\n"); p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO, - "scaletempo", NULL); + "scaletempo2", NULL); } else if (req_filter == 2) { MP_VERBOSE(f, "adding drop\n"); p->sub.filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO, diff --git a/filters/f_auto_filters.h b/filters/f_auto_filters.h index 98043c9301..f926f6e449 100644 --- a/filters/f_auto_filters.h +++ b/filters/f_auto_filters.h @@ -9,5 +9,7 @@ struct mp_filter *mp_deint_create(struct mp_filter *parent); // Rotate according to mp_image.rotate and VO capabilities. struct mp_filter *mp_autorotate_create(struct mp_filter *parent); -// Insert a filter that inserts scaletempo depending on speed settings. +// Insert a filter that inserts scaletempo2 depending on speed settings. struct mp_filter *mp_autoaspeed_create(struct mp_filter *parent); + +bool mp_deint_active(struct mp_filter *parent); diff --git a/filters/f_autoconvert.c b/filters/f_autoconvert.c index 7452a13ae8..e045d74c96 100644 --- a/filters/f_autoconvert.c +++ b/filters/f_autoconvert.c @@ -1,5 +1,3 @@ -#include "config.h" - #include "audio/aframe.h" #include "audio/chmap_sel.h" #include "audio/format.h" @@ -152,9 +150,14 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log, for (int n = 0; n < p->num_imgfmts; n++) { bool samefmt = img->params.imgfmt == p->imgfmts[n]; bool samesubffmt = img->params.hw_subfmt == p->subfmts[n]; - if (samefmt && (samesubffmt || !p->subfmts[n])) { + /* + * In practice, `p->subfmts` is not usually populated today, in which + * case we must actively probe formats below to establish if the VO can + * accept the subfmt being used by the hwdec. + */ + if (samefmt && samesubffmt) { if (p->imgparams_set) { - if (!mp_image_params_equal(&p->imgparams, &img->params)) + if (!mp_image_params_static_equal(&p->imgparams, &img->params)) break; } return true; @@ -162,6 +165,9 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log, } struct mp_filter *conv = mp_filter_create(f, &convert_filter); + if (!conv) + return false; + mp_filter_add_pin(conv, MP_PIN_IN, "in"); mp_filter_add_pin(conv, MP_PIN_OUT, "out"); @@ -185,27 +191,77 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log, bool dst_all_hw = true; bool dst_have_sw = false; + bool has_src_hw_fmt = false; for (int n = 0; n < num_fmts; n++) { bool is_hw = IMGFMT_IS_HWACCEL(fmts[n]); dst_all_hw &= is_hw; dst_have_sw |= !is_hw; + has_src_hw_fmt |= is_hw && fmts[n] == imgpar.imgfmt; } - // Source is sw, all targets are hw -> try to upload. - bool sw_to_hw = imgfmt_is_sw && dst_all_hw; // Source is hw, some targets are sw -> try to download. bool hw_to_sw = !imgfmt_is_sw && dst_have_sw; - if (sw_to_hw && num_fmts > 0) { - // We can probably use this! Very lazy and very approximate. - struct mp_hwupload *upload = mp_hwupload_create(conv, fmts[0]); - if (upload) { - mp_info(log, "HW-uploading to %s\n", mp_imgfmt_to_name(fmts[0])); - filters[2] = upload->f; - hwupload_fmt = mp_hwupload_find_upload_format(upload, img->imgfmt); - fmts = &hwupload_fmt; - num_fmts = hwupload_fmt ? 1 : 0; + if (has_src_hw_fmt) { + int src_fmt = img->params.hw_subfmt; + /* + * If the source format is a hardware format, and our output supports + * that hardware format, we prioritize preserving the use of that + * hardware format. In most cases, the sub format will also be supported + * and no conversion will be required, but in some cases, the hwdec + * may be able to output formats that the VO cannot display, and + * hardware format conversion becomes necessary. + */ + struct mp_hwupload upload = mp_hwupload_create(conv, imgpar.imgfmt, + src_fmt, + true); + if (upload.successful_init) { + if (upload.f) { + mp_info(log, "Converting %s[%s] -> %s[%s]\n", + mp_imgfmt_to_name(imgpar.imgfmt), + mp_imgfmt_to_name(src_fmt), + mp_imgfmt_to_name(imgpar.imgfmt), + mp_imgfmt_to_name(upload.selected_sw_imgfmt)); + filters[2] = upload.f; + } hw_to_sw = false; + need_sws = false; + } else { + mp_err(log, "Failed to create HW uploader for format %s\n", + mp_imgfmt_to_name(src_fmt)); + } + } else if (dst_all_hw && num_fmts > 0) { + bool upload_created = false; + int sw_fmt = imgfmt_is_sw ? img->imgfmt : img->params.hw_subfmt; + + for (int i = 0; i < num_fmts; i++) { + // We can probably use this! Very lazy and very approximate. + struct mp_hwupload upload = mp_hwupload_create(conv, fmts[i], + sw_fmt, false); + if (upload.successful_init) { + mp_info(log, "HW-uploading to %s\n", mp_imgfmt_to_name(fmts[i])); + filters[2] = upload.f; + hwupload_fmt = upload.selected_sw_imgfmt; + fmts = &hwupload_fmt; + num_fmts = hwupload_fmt ? 1 : 0; + hw_to_sw = false; + + // We cannot do format conversions when transferring between + // two hardware devices, so reject this format if that would be + // required. + if (!imgfmt_is_sw && hwupload_fmt != sw_fmt) { + mp_err(log, "Format %s is not supported by %s\n", + mp_imgfmt_to_name(sw_fmt), + mp_imgfmt_to_name(p->imgfmts[i])); + continue; + } + upload_created = true; + break; + } + } + if (!upload_created) { + mp_err(log, "Failed to create HW uploader for format %s\n", + mp_imgfmt_to_name(sw_fmt)); } } @@ -235,6 +291,11 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log, force_sws_params |= !mp_image_params_equal(&imgpar, &p->imgparams); need_sws |= force_sws_params; } + if (!imgfmt_is_sw && dst_all_hw) { + // This is a hw -> hw upload, so the sw format must already be + // mutually understood. No conversion can be done. + need_sws = false; + } if (need_sws) { // Create a new conversion filter. @@ -300,8 +361,8 @@ static void handle_video_frame(struct mp_filter *f) } if (!mp_subfilter_drain_destroy(&p->sub)) { - p->in_imgfmt = p->in_subfmt = 0; - return; + MP_VERBOSE(f, "Sub-filter requires draining but we must destroy it now.\n"); + mp_subfilter_destroy(&p->sub); } p->in_imgfmt = img->params.imgfmt; @@ -416,7 +477,7 @@ cont: mp_subfilter_continue(&p->sub); } -static void process(struct mp_filter *f) +static void autoconvert_process(struct mp_filter *f) { struct priv *p = f->priv; @@ -447,7 +508,7 @@ void mp_autoconvert_format_change_continue(struct mp_autoconvert *c) } } -static bool command(struct mp_filter *f, struct mp_filter_command *cmd) +static bool autoconvert_command(struct mp_filter *f, struct mp_filter_command *cmd) { struct priv *p = f->priv; @@ -468,7 +529,7 @@ static bool command(struct mp_filter *f, struct mp_filter_command *cmd) return false; } -static void reset(struct mp_filter *f) +static void autoconvert_reset(struct mp_filter *f) { struct priv *p = f->priv; @@ -478,7 +539,7 @@ static void reset(struct mp_filter *f) p->format_change_blocked = false; } -static void destroy(struct mp_filter *f) +static void autoconvert_destroy(struct mp_filter *f) { struct priv *p = f->priv; @@ -489,10 +550,10 @@ static void destroy(struct mp_filter *f) static const struct mp_filter_info autoconvert_filter = { .name = "autoconvert", .priv_size = sizeof(struct priv), - .process = process, - .command = command, - .reset = reset, - .destroy = destroy, + .process = autoconvert_process, + .command = autoconvert_command, + .reset = autoconvert_reset, + .destroy = autoconvert_destroy, }; struct mp_autoconvert *mp_autoconvert_create(struct mp_filter *parent) diff --git a/filters/f_decoder_wrapper.c b/filters/f_decoder_wrapper.c index 397c3a0a98..7abe95116d 100644 --- a/filters/f_decoder_wrapper.c +++ b/filters/f_decoder_wrapper.c @@ -21,13 +21,11 @@ #include <stdbool.h> #include <math.h> #include <assert.h> -#include <pthread.h> #include <libavutil/buffer.h> #include <libavutil/common.h> #include <libavutil/rational.h> -#include "config.h" #include "options/options.h" #include "common/msg.h" #include "options/m_config.h" @@ -54,7 +52,7 @@ #include "filter_internal.h" struct dec_queue_opts { - int use_queue; + bool use_queue; int64_t max_bytes; int64_t max_samples; double max_duration; @@ -63,7 +61,7 @@ struct dec_queue_opts { #define OPT_BASE_STRUCT struct dec_queue_opts static const struct m_option dec_queue_opts_list[] = { - {"enable", OPT_FLAG(use_queue)}, + {"enable", OPT_BOOL(use_queue)}, {"max-secs", OPT_DOUBLE(max_duration), M_RANGE(0, DBL_MAX)}, {"max-bytes", OPT_BYTE_SIZE(max_bytes), M_RANGE(0, M_MAX_MEM_BYTES)}, {"max-samples", OPT_INT64(max_samples), M_RANGE(0, DBL_MAX)}, @@ -74,7 +72,6 @@ static const struct m_sub_options vdec_queue_conf = { .opts = dec_queue_opts_list, .size = sizeof(struct dec_queue_opts), .defaults = &(const struct dec_queue_opts){ - .use_queue = 0, .max_bytes = 512 * 1024 * 1024, .max_samples = 50, .max_duration = 2, @@ -85,7 +82,6 @@ static const struct m_sub_options adec_queue_conf = { .opts = dec_queue_opts_list, .size = sizeof(struct dec_queue_opts), .defaults = &(const struct dec_queue_opts){ - .use_queue = 0, .max_bytes = 1 * 1024 * 1024, .max_samples = 48000, .max_duration = 1, @@ -96,10 +92,10 @@ static const struct m_sub_options adec_queue_conf = { #define OPT_BASE_STRUCT struct dec_wrapper_opts struct dec_wrapper_opts { - float movie_aspect; + double movie_aspect; int aspect_method; - double force_fps; - int correct_pts; + double fps_override; + bool correct_pts; int video_rotate; char *audio_decoders; char *video_decoders; @@ -110,16 +106,19 @@ struct dec_wrapper_opts { int64_t audio_reverse_size; }; -static int decoder_list_opt(struct mp_log *log, const m_option_t *opt, - struct bstr name, struct bstr param); +static int decoder_list_help(struct mp_log *log, const m_option_t *opt, + struct bstr name); const struct m_sub_options dec_wrapper_conf = { .opts = (const struct m_option[]){ - {"correct-pts", OPT_FLAG(correct_pts)}, - {"fps", OPT_DOUBLE(force_fps), M_RANGE(0, DBL_MAX)}, - {"ad", OPT_STRING_VALIDATE(audio_decoders, decoder_list_opt)}, - {"vd", OPT_STRING_VALIDATE(video_decoders, decoder_list_opt)}, - {"audio-spdif", OPT_STRING_VALIDATE(audio_spdif, decoder_list_opt)}, + {"correct-pts", OPT_BOOL(correct_pts)}, + {"container-fps-override", OPT_DOUBLE(fps_override), M_RANGE(0, DBL_MAX)}, + {"ad", OPT_STRING(audio_decoders), + .help = decoder_list_help}, + {"vd", OPT_STRING(video_decoders), + .help = decoder_list_help}, + {"audio-spdif", OPT_STRING(audio_spdif), + .help = decoder_list_help}, {"video-rotate", OPT_CHOICE(video_rotate, {"no", -1}), .flags = UPDATE_IMGPAR, M_RANGE(0, 359)}, {"video-aspect-override", OPT_ASPECT(movie_aspect), @@ -132,12 +131,13 @@ const struct m_sub_options dec_wrapper_conf = { {"video-reversal-buffer", OPT_BYTE_SIZE(video_reverse_size), M_RANGE(0, M_MAX_MEM_BYTES)}, {"audio-reversal-buffer", OPT_BYTE_SIZE(audio_reverse_size), - M_RANGE(0, M_MAX_MEM_BYTES)} , + M_RANGE(0, M_MAX_MEM_BYTES)}, + {"fps", OPT_REPLACED("container-fps-override")}, {0} }, .size = sizeof(struct dec_wrapper_opts), .defaults = &(const struct dec_wrapper_opts){ - .correct_pts = 1, + .correct_pts = true, .movie_aspect = -1., .aspect_method = 2, .video_reverse_size = 1 * 1024 * 1024 * 1024, @@ -218,13 +218,12 @@ struct priv { struct mp_async_queue *queue; // decoded frame output queue struct mp_dispatch_queue *dec_dispatch; // non-NULL if decoding thread used bool dec_thread_lock; // debugging (esp. for no-thread case) - pthread_t dec_thread; + mp_thread dec_thread; bool dec_thread_valid; - pthread_mutex_t cache_lock; + mp_mutex cache_lock; // --- Protected by cache_lock. char *cur_hwdec; - char *decoder_desc; bool try_spdif; bool attached_picture; bool pts_reset; @@ -232,11 +231,9 @@ struct priv { int dropped_frames; // total frames _probably_ dropped }; -static int decoder_list_opt(struct mp_log *log, const m_option_t *opt, - struct bstr name, struct bstr param) +static int decoder_list_help(struct mp_log *log, const m_option_t *opt, + struct bstr name) { - if (!bstr_equals0(param, "help")) - return 1; if (strcmp(opt->name, "ad") == 0) { struct mp_decoder_list *list = audio_decoder_list(); mp_print_decoders(log, MSGL_INFO, "Audio decoders:", list); @@ -260,13 +257,13 @@ static int decoder_list_opt(struct mp_log *log, const m_option_t *opt, // thread state. Must run on/locked with decoder thread. static void update_cached_values(struct priv *p) { - pthread_mutex_lock(&p->cache_lock); + mp_mutex_lock(&p->cache_lock); p->cur_hwdec = NULL; if (p->decoder && p->decoder->control) p->decoder->control(p->decoder->f, VDCTRL_GET_HWDEC, &p->cur_hwdec); - pthread_mutex_unlock(&p->cache_lock); + mp_mutex_unlock(&p->cache_lock); } // Lock the decoder thread. This may synchronously wait until the decoder thread @@ -325,11 +322,11 @@ static void decf_reset(struct mp_filter *f) p->pts = MP_NOPTS_VALUE; p->last_format = p->fixed_format = (struct mp_image_params){0}; - pthread_mutex_lock(&p->cache_lock); + mp_mutex_lock(&p->cache_lock); p->pts_reset = false; p->attempt_framedrops = 0; p->dropped_frames = 0; - pthread_mutex_unlock(&p->cache_lock); + mp_mutex_unlock(&p->cache_lock); p->coverart_returned = 0; @@ -348,9 +345,9 @@ int mp_decoder_wrapper_control(struct mp_decoder_wrapper *d, struct priv *p = d->f->priv; int res = CONTROL_UNKNOWN; if (cmd == VDCTRL_GET_HWDEC) { - pthread_mutex_lock(&p->cache_lock); + mp_mutex_lock(&p->cache_lock); *(char **)arg = p->cur_hwdec; - pthread_mutex_unlock(&p->cache_lock); + mp_mutex_unlock(&p->cache_lock); } else { thread_lock(p); if (p->decoder && p->decoder->control) @@ -399,9 +396,6 @@ static bool reinit_decoder(struct priv *p) reset_decoder(p); p->has_broken_packet_pts = -10; // needs 10 packets to reach decision - talloc_free(p->decoder_desc); - p->decoder_desc = NULL; - const struct mp_decoder_fns *driver = NULL; struct mp_decoder_list *list = NULL; char *user_list = NULL; @@ -416,9 +410,9 @@ static bool reinit_decoder(struct priv *p) user_list = p->opts->audio_decoders; fallback = "aac"; - pthread_mutex_lock(&p->cache_lock); + mp_mutex_lock(&p->cache_lock); bool try_spdif = p->try_spdif; - pthread_mutex_unlock(&p->cache_lock); + mp_mutex_unlock(&p->cache_lock); if (try_spdif && p->codec->codec) { struct mp_decoder_list *spdif = @@ -432,10 +426,12 @@ static bool reinit_decoder(struct priv *p) } } + if (!driver) + return false; + if (!list) { struct mp_decoder_list *full = talloc_zero(NULL, struct mp_decoder_list); - if (driver) - driver->add_decoders(full); + driver->add_decoders(full); const char *codec = p->codec->codec; if (codec && strcmp(codec, "null") == 0) codec = fallback; @@ -451,11 +447,12 @@ static bool reinit_decoder(struct priv *p) |