diff options
Diffstat (limited to 'filters/f_autoconvert.c')
-rw-r--r-- | filters/f_autoconvert.c | 113 |
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) |