summaryrefslogtreecommitdiffstats
path: root/filters/f_autoconvert.c
diff options
context:
space:
mode:
Diffstat (limited to 'filters/f_autoconvert.c')
-rw-r--r--filters/f_autoconvert.c113
1 files changed, 88 insertions, 25 deletions
diff --git a/filters/f_autoconvert.c b/filters/f_autoconvert.c
index 5e0caaf321..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.
@@ -244,6 +305,8 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log,
goto fail;
}
+ sws->force_scaler = c->force_scaler;
+
int out = mp_sws_find_best_out_format(sws, src_fmt, fmts, num_fmts);
if (!out) {
mp_err(log, "can't find video conversion for %s\n",
@@ -298,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;
@@ -414,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;
@@ -445,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;
@@ -466,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;
@@ -476,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;
@@ -487,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)