diff options
Diffstat (limited to 'filters')
-rw-r--r-- | filters/f_async_queue.c | 124 | ||||
-rw-r--r-- | filters/f_async_queue.h | 47 | ||||
-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 | 237 | ||||
-rw-r--r-- | filters/f_decoder_wrapper.h | 10 | ||||
-rw-r--r-- | filters/f_demux_in.c | 14 | ||||
-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 | 85 | ||||
-rw-r--r-- | filters/filter.h | 11 | ||||
-rw-r--r-- | filters/filter_internal.h | 3 | ||||
-rw-r--r-- | filters/user_filters.c | 21 | ||||
-rw-r--r-- | filters/user_filters.h | 1 |
24 files changed, 1060 insertions, 464 deletions
diff --git a/filters/f_async_queue.c b/filters/f_async_queue.c index 696649f3d1..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; @@ -88,6 +88,8 @@ static int64_t frame_get_samples(struct async_queue *q, struct mp_frame frame) struct mp_aframe *aframe = frame.data; res = mp_aframe_get_size(aframe); } + if (mp_frame_is_signaling(frame)) + return 0; return res; } @@ -140,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) @@ -153,22 +155,76 @@ void mp_async_queue_reset(struct mp_async_queue *queue) reset_queue(queue->q); } +bool mp_async_queue_is_active(struct mp_async_queue *queue) +{ + struct async_queue *q = queue->q; + mp_mutex_lock(&q->lock); + bool res = q->active; + mp_mutex_unlock(&q->lock); + return res; +} + +bool mp_async_queue_is_full(struct mp_async_queue *queue) +{ + struct async_queue *q = queue->q; + mp_mutex_lock(&q->lock); + bool res = is_full(q); + mp_mutex_unlock(&q->lock); + return res; +} + 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; + + mp_mutex_lock(&q->lock); + if (!q->active || !q->reading) { + q->active = true; + q->reading = true; + // Possibly start producer/consumer. + for (int n = 0; n < 2; n++) { + if (q->conn[n]) + mp_filter_wakeup(q->conn[n]); + } + } + mp_mutex_unlock(&q->lock); +} + +int64_t mp_async_queue_get_samples(struct mp_async_queue *queue) +{ + struct async_queue *q = queue->q; + mp_mutex_lock(&q->lock); + int64_t res = q->samples_size; + mp_mutex_unlock(&q->lock); + return res; +} + +int mp_async_queue_get_frames(struct mp_async_queue *queue) +{ + struct async_queue *q = queue->q; + mp_mutex_lock(&q->lock); + int res = q->num_frames; + mp_mutex_unlock(&q->lock); + return res; } struct priv { struct async_queue *q; + struct mp_filter *notify; }; static void destroy(struct mp_filter *f) @@ -176,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); } @@ -192,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. @@ -210,10 +266,15 @@ static void process_in(struct mp_filter *f) // Notify reader that we have new frames. if (q->conn[1]) mp_filter_wakeup(q->conn[1]); - if (!is_full(q)) + bool full = is_full(q); + if (!full) mp_pin_out_request_data_next(f->ppins[0]); + if (p->notify && full) + mp_filter_wakeup(p->notify); } - pthread_mutex_unlock(&q->lock); + if (p->notify && !q->num_frames) + mp_filter_wakeup(p->notify); + mp_mutex_unlock(&q->lock); } static void process_out(struct mp_filter *f) @@ -225,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]); @@ -240,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) @@ -248,7 +309,12 @@ static void reset(struct mp_filter *f) struct priv *p = f->priv; struct async_queue *q = p->q; - reset_queue(q); + 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); + mp_mutex_unlock(&q->lock); } // producer @@ -266,9 +332,19 @@ static const struct mp_filter_info info_out = { .priv_size = sizeof(struct priv), .destroy = destroy, .process = process_out, - .reset = reset, }; +void mp_async_queue_set_notifier(struct mp_filter *f, struct mp_filter *notify) +{ + assert(mp_filter_get_info(f) == &info_in); + struct priv *p = f->priv; + if (p->notify != notify) { + p->notify = notify; + if (notify) + mp_filter_wakeup(notify); + } +} + struct mp_filter *mp_async_queue_create_filter(struct mp_filter *parent, enum mp_pin_dir dir, struct mp_async_queue *queue) @@ -289,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 6b1ffabe36..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 @@ -24,8 +25,32 @@ void mp_async_queue_reset(struct mp_async_queue *queue); // Put the queue into "active" mode. If it wasn't, then the consumer is woken // up (and if there is no data in the queue, this will in turn wake up the // producer, i.e. start transfers automatically). +// If there is a writer end but no reader end, this will simply make the queue +// fill up. void mp_async_queue_resume(struct mp_async_queue *queue); +// Like mp_async_queue_resume(), but also allows the producer writing to the +// queue, even if the consumer will request any data yet. +void mp_async_queue_resume_reading(struct mp_async_queue *queue); + +// Returns true if out of mp_async_queue_reset()/mp_async_queue_resume(), the +// latter was most recently called. +bool mp_async_queue_is_active(struct mp_async_queue *queue); + +// Returns true if the queue reached its configured size, the input filter +// accepts no further frames. Always returns false if not active (then it does +// not accept any input at all). +bool mp_async_queue_is_full(struct mp_async_queue *queue); + +// Get the total of samples buffered within the queue itself. This doesn't count +// samples buffered in the access filters. mp_async_queue_config.sample_unit is +// used to define what "1 sample" means. +int64_t mp_async_queue_get_samples(struct mp_async_queue *queue); + +// Get the total number of frames buffered within the queue itself. Frames +// buffered in the access filters are not included. +int mp_async_queue_get_frames(struct mp_async_queue *queue); + // Create a filter to access the queue, and connect it. It's not allowed to // connect an already connected end of the queue. The filter can be freed at // any time. @@ -34,11 +59,17 @@ void mp_async_queue_resume(struct mp_async_queue *queue); // the producer to write any data. You need to call mp_async_queue_resume() to // start communication. Actual transfers happen only once the consumer filter // has read requests on its mp_pin. -// Resetting any of the consumer/producer filters calls mp_async_queue_reset(). // If the producer filter requested a new frame from its filter graph, and the // queue is asynchronously set to "inactive", then the requested frame will be // silently discarded once it reaches the producer filter. // +// Resetting a queue filter does not affect the queue at all. Managing the +// queue state is the API user's responsibility. Note that resetting an input +// filter (dir==MP_PIN_IN) while the queue is active and in "reading" state +// (the output filter requested data at any point before the last +// mp_async_queue_reset(), or mp_async_queue_resume_reading() was called), the +// filter will immediately request data after the reset. +// // For proper global reset, this order should be preferred: // - mp_async_queue_reset() // - reset producer and consumer filters on their respective threads (in any @@ -55,9 +86,21 @@ struct mp_filter *mp_async_queue_create_filter(struct mp_filter *parent, enum mp_pin_dir dir, struct mp_async_queue *queue); +// Set a filter that should be woken up with mp_filter_wakeup() in the following +// situations: +// - mp_async_queue_is_full() changes to true (at least for a short moment) +// - mp_async_queue_get_frames() changes to 0 (at least until new data is fed) +// This is a workaround for the filter design, which does not allow you to write +// to the queue in a "sequential" way (write, then check condition). +// Calling this again on the same filter removes the previous notify filter. +// f: must be a filter returned by mp_async_queue_create_filter(, MP_PIN_IN,) +// notify: filter to be woken up +void mp_async_queue_set_notifier(struct mp_filter *f, struct mp_filter *notify); + enum mp_async_queue_sample_unit { AQUEUE_UNIT_FRAME = 0, // a frame counts as 1 sample - AQUEUE_UNIT_SAMPLES, // number of audio samples (1 for other media types) + AQUEUE_UNIT_SAMPLES, // number of audio samples (1 for other media types, + // 0 for signaling) }; // Setting this struct to all-0 is equivalent to defaults. 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 3f7fe50aad..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 = dec |