summaryrefslogtreecommitdiffstats
path: root/filters
diff options
context:
space:
mode:
Diffstat (limited to 'filters')
-rw-r--r--filters/f_async_queue.c124
-rw-r--r--filters/f_async_queue.h47
-rw-r--r--filters/f_auto_filters.c69
-rw-r--r--filters/f_auto_filters.h4
-rw-r--r--filters/f_autoconvert.c111
-rw-r--r--filters/f_decoder_wrapper.c237
-rw-r--r--filters/f_decoder_wrapper.h10
-rw-r--r--filters/f_demux_in.c14
-rw-r--r--filters/f_hwtransfer.c499
-rw-r--r--filters/f_hwtransfer.h17
-rw-r--r--filters/f_lavfi.c108
-rw-r--r--filters/f_lavfi.h3
-rw-r--r--filters/f_output_chain.c73
-rw-r--r--filters/f_output_chain.h3
-rw-r--r--filters/f_swresample.c62
-rw-r--r--filters/f_swresample.h4
-rw-r--r--filters/f_swscale.c10
-rw-r--r--filters/f_utils.c2
-rw-r--r--filters/f_utils.h6
-rw-r--r--filters/filter.c85
-rw-r--r--filters/filter.h11
-rw-r--r--filters/filter_internal.h3
-rw-r--r--filters/user_filters.c21
-rw-r--r--filters/user_filters.h1
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