summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-10-02 22:30:25 +0200
committerwm4 <wm4@nowhere>2019-10-02 22:30:25 +0200
commit55424c29d35ec96061c36537e1ad18cd9a281b5a (patch)
tree4cd9d600b092e2aa4a74b6eea858fc14f53d6271
parent3e02f39087029380d666cfda963084d3952582f8 (diff)
downloadmpv-55424c29d35ec96061c36537e1ad18cd9a281b5a.tar.bz2
mpv-55424c29d35ec96061c36537e1ad18cd9a281b5a.tar.xz
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.
-rw-r--r--filters/f_autoconvert.c82
-rw-r--r--filters/f_autoconvert.h7
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).