From 55424c29d35ec96061c36537e1ad18cd9a281b5a Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 2 Oct 2019 22:30:25 +0200 Subject: f_autoconvert: add hw->sw download path For some reason it could do sw->sw and sw->hw (and, in some ways, even do hw->hw in special cases), but not hw->sw. Add this. --- filters/f_autoconvert.c | 82 ++++++++++++++++++++++++++++++++++++++----------- filters/f_autoconvert.h | 7 +++++ 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/filters/f_autoconvert.c b/filters/f_autoconvert.c index b441a2b50d..2e039c260c 100644 --- a/filters/f_autoconvert.c +++ b/filters/f_autoconvert.c @@ -7,6 +7,7 @@ #include "common/msg.h" #include "video/hwdec.h" #include "video/mp_image.h" +#include "video/mp_image_pool.h" #include "f_autoconvert.h" #include "f_hwtransfer.h" @@ -95,6 +96,14 @@ void mp_autoconvert_add_imgfmt(struct mp_autoconvert *c, int imgfmt, int subfmt) p->force_update = true; } +void mp_autoconvert_add_all_sw_imgfmts(struct mp_autoconvert *c) +{ + for (int n = IMGFMT_START; n < IMGFMT_END; n++) { + if (!IMGFMT_IS_HWACCEL(n)) + mp_autoconvert_add_imgfmt(c, n, 0); + } +} + void mp_autoconvert_add_vo_hwdec_subfmts(struct mp_autoconvert *c, struct mp_hwdec_devices *devs) { @@ -207,39 +216,73 @@ static void handle_video_frame(struct mp_filter *f) mp_filter_add_pin(conv, MP_PIN_IN, "in"); mp_filter_add_pin(conv, MP_PIN_OUT, "out"); - struct mp_filter *filters[2] = {0}; + // 0: hw->sw download + // 1: swscale + // 2: sw->hw upload + struct mp_filter *filters[3] = {0}; bool need_sws = true; int *fmts = p->imgfmts; int num_fmts = p->num_imgfmts; + bool imgfmt_is_sw = !IMGFMT_IS_HWACCEL(img->imgfmt); + + // This should not happen. But not enough guarantee to make it an assert(). + if (imgfmt_is_sw != !img->hwctx) + MP_WARN(p, "Unexpected AVFrame/imgfmt hardware context mismatch.\n"); + + bool dst_all_hw = true; + bool dst_have_sw = 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; + } + // Source is sw, all targets are hw -> try to upload. - bool sw_to_hw = !IMGFMT_IS_HWACCEL(img->imgfmt); - for (int n = 0; n < num_fmts; n++) - sw_to_hw &= IMGFMT_IS_HWACCEL(fmts[n]); + 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(p, "HW-uploading to %s\n", mp_imgfmt_to_name(fmts[0])); - filters[1] = upload->f; + filters[2] = upload->f; fmts = upload->upload_fmts; num_fmts = upload->num_upload_fmts; + hw_to_sw = false; } } else if (p->vo_convert && different_subfmt && info && info->hwdec_devs) { for (int n = 0; subfmt_converters[n].hw_imgfmt; n++) { if (subfmt_converters[n].hw_imgfmt == img->imgfmt) { MP_INFO(p, "Using HW sub-conversion.\n"); - filters[1] = subfmt_converters[n].create(conv); - if (filters[1]) { + filters[2] = subfmt_converters[n].create(conv); + if (filters[2]) { need_sws = false; + hw_to_sw = false; break; } } } } + int src_fmt = img->imgfmt; + if (hw_to_sw) { + MP_INFO(p, "HW-downloading from %s\n", mp_imgfmt_to_name(img->imgfmt)); + int res_fmt = mp_image_hw_download_get_sw_format(img); + if (!res_fmt) { + MP_ERR(p, "cannot copy surface of this format to CPU memory\n"); + goto fail; + } + struct mp_hwdownload *hwd = mp_hwdownload_create(conv); + if (hwd) { + filters[0] = hwd->f; + src_fmt = res_fmt; + } + } + if (need_sws) { // Create a new conversion filter. struct mp_sws_filter *sws = mp_sws_filter_create(conv); @@ -248,31 +291,34 @@ static void handle_video_frame(struct mp_filter *f) return; } - int out = mp_sws_find_best_out_format(img->imgfmt, fmts, num_fmts); + int out = mp_sws_find_best_out_format(src_fmt, fmts, num_fmts); if (!out) { - MP_ERR(p, "can't find video conversion for %s/%s\n", - mp_imgfmt_to_name(img->imgfmt), - mp_imgfmt_to_name(img->params.hw_subfmt)); - talloc_free(conv); - mp_filter_internal_mark_failed(f); - return; + MP_ERR(p, "can't find video conversion for %s\n", + mp_imgfmt_to_name(src_fmt)); + goto fail; } - if (out == img->imgfmt) { + if (out == src_fmt) { // Can happen if hwupload goes to same format. talloc_free(sws->f); } else { sws->out_format = out; - MP_INFO(p, "Converting %s -> %s\n", mp_imgfmt_to_name(img->imgfmt), + MP_INFO(p, "Converting %s -> %s\n", mp_imgfmt_to_name(src_fmt), mp_imgfmt_to_name(sws->out_format)); - filters[0] = sws->f; + filters[1] = sws->f; } } - mp_chain_filters(conv->ppins[0], conv->ppins[1], filters, 2); + mp_chain_filters(conv->ppins[0], conv->ppins[1], filters, 3); p->sub.filter = conv; mp_subfilter_continue(&p->sub); + return; + +fail: + talloc_free(conv); + mp_filter_internal_mark_failed(f); + return; } static void handle_audio_frame(struct mp_filter *f) diff --git a/filters/f_autoconvert.h b/filters/f_autoconvert.h index 4cd0cb3d6d..d8fac346e5 100644 --- a/filters/f_autoconvert.h +++ b/filters/f_autoconvert.h @@ -27,6 +27,13 @@ struct mp_autoconvert *mp_autoconvert_create(struct mp_filter *parent); // otherwise must be 0. void mp_autoconvert_add_imgfmt(struct mp_autoconvert *c, int imgfmt, int subfmt); +// Add all sw image formats. The effect is that hardware video image formats are +// disallowed. The semantics are the same as calling mp_autoconvert_add_imgfmt() +// for each sw format that exists. +// No need to do this if you add sw formats with mp_autoconvert_add_imgfmt(), +// as the normal semantics will exclude other formats (including hw ones). +void mp_autoconvert_add_all_sw_imgfmts(struct mp_autoconvert *c); + // Add the formats supported by the hwdec interops (or essentially refine them), // and trigger conversion if hw_subfmts mismatch. This is mostly a hack for // D3D11/ANGLE (which supports NV12 only). -- cgit v1.2.3