summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--filters/f_autoconvert.c48
-rw-r--r--filters/f_hwtransfer.c110
-rw-r--r--filters/f_hwtransfer.h17
-rw-r--r--video/hwdec.h5
-rw-r--r--video/out/hwdec/hwdec_vaapi.c3
5 files changed, 135 insertions, 48 deletions
diff --git a/filters/f_autoconvert.c b/filters/f_autoconvert.c
index b2db4e7f9e..739a45b496 100644
--- a/filters/f_autoconvert.c
+++ b/filters/f_autoconvert.c
@@ -150,7 +150,12 @@ 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))
break;
@@ -183,26 +188,57 @@ 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 hw, some targets are sw -> try to download.
bool hw_to_sw = !imgfmt_is_sw && dst_have_sw;
- if (dst_all_hw && num_fmts > 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]);
- if (upload) {
+ 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 = mp_hwupload_find_upload_format(upload, sw_fmt);
+ filters[2] = upload.f;
+ hwupload_fmt = upload.selected_sw_imgfmt;
fmts = &hwupload_fmt;
num_fmts = hwupload_fmt ? 1 : 0;
hw_to_sw = false;
diff --git a/filters/f_hwtransfer.c b/filters/f_hwtransfer.c
index b281a15838..a8f01e4054 100644
--- a/filters/f_hwtransfer.c
+++ b/filters/f_hwtransfer.c
@@ -1,6 +1,7 @@
#include <libavutil/buffer.h>
#include <libavutil/hwcontext.h>
#include <libavutil/mem.h>
+#include <libavutil/pixdesc.h>
#include "video/fmt-conversion.h"
#include "video/hwdec.h"
@@ -8,7 +9,10 @@
#include "video/mp_image_pool.h"
#include "f_hwtransfer.h"
+#include "f_output_chain.h"
+#include "f_utils.h"
#include "filter_internal.h"
+#include "user_filters.h"
struct priv {
AVBufferRef *av_device_ctx;
@@ -39,7 +43,9 @@ struct priv {
int *map_fmts;
int num_map_fmts;
- struct mp_hwupload public;
+ // If the selected hwdec has a conversion filter available for converting
+ // between sw formats in hardware, the name will be set. NULL otherwise.
+ const char *conversion_filter_name;
};
struct hwmap_pairs {
@@ -66,6 +72,11 @@ static const struct hwmap_pairs hwmap_pairs[] = {
/**
* @brief Find the closest supported format when hw uploading
*
+ * Return the best format suited for upload that is supported for a given input
+ * imgfmt. This returns the same as imgfmt if the format is natively supported,
+ * and otherwise a format that likely results in the least loss.
+ * Returns 0 if completely unsupported.
+ *
* Some hardware types support implicit format conversion on upload. For these
* types, it is possible for the set of formats that are accepts as inputs to
* the upload process to differ from the set of formats that can be outputs of
@@ -122,23 +133,6 @@ static bool select_format(struct priv *p, int input_fmt,
return true;
}
-int mp_hwupload_find_upload_format(struct mp_hwupload *u, int imgfmt)
-{
- struct priv *p = u->f->priv;
-
- int sw = 0, up = 0;
- select_format(p, imgfmt, &sw, &up);
- // In th case where the imgfmt is not natively supported, it must be
- // converted, either before or during upload. If the imgfmt is supported as
- // an hw input format, then prefer that, and if the upload has to do implicit
- // conversion, that's fine. On the other hand, if the imgfmt is not a
- // supported input format, then pick the output format as the conversion
- // target to avoid doing two conversions (one before upload, and one during
- // upload). Note that for most hardware types, there is no ability to convert
- // during upload, and the two formats will always be the same.
- return imgfmt == sw ? sw : up;
-}
-
static void process(struct mp_filter *f)
{
struct priv *p = f->priv;
@@ -266,17 +260,17 @@ static bool vo_supports(struct mp_hwdec_ctx *ctx, int hw_fmt, int sw_fmt)
return false;
}
-static bool probe_formats(struct mp_hwupload *u, int hw_imgfmt)
+static bool probe_formats(struct mp_filter *f, int hw_imgfmt)
{
- struct priv *p = u->f->priv;
+ struct priv *p = f->priv;
p->hw_imgfmt = hw_imgfmt;
p->num_fmts = 0;
p->num_upload_fmts = 0;
- struct mp_stream_info *info = mp_filter_find_stream_info(u->f);
+ struct mp_stream_info *info = mp_filter_find_stream_info(f);
if (!info || !info->hwdec_devs) {
- MP_ERR(u->f, "no hw context\n");
+ MP_ERR(f, "no hw context\n");
return false;
}
@@ -312,7 +306,7 @@ static bool probe_formats(struct mp_hwupload *u, int hw_imgfmt)
}
if (!ctx) {
- MP_INFO(u->f, "no support for this hw format\n");
+ MP_INFO(f, "no support for this hw format\n");
return false;
}
@@ -339,14 +333,14 @@ static bool probe_formats(struct mp_hwupload *u, int hw_imgfmt)
if (!imgfmt)
continue;
- MP_VERBOSE(u->f, "looking at format %s/%s\n",
+ MP_VERBOSE(f, "looking at format %s/%s\n",
mp_imgfmt_to_name(hw_imgfmt),
mp_imgfmt_to_name(imgfmt));
if (IMGFMT_IS_HWACCEL(imgfmt)) {
// If the enumerated format is a hardware format, we don't need to
// do any further probing. It will be supported.
- MP_VERBOSE(u->f, " supports %s (a hardware format)\n",
+ MP_VERBOSE(f, " supports %s (a hardware format)\n",
mp_imgfmt_to_name(imgfmt));
continue;
}
@@ -356,7 +350,7 @@ static bool probe_formats(struct mp_hwupload *u, int hw_imgfmt)
if (!mp_update_av_hw_frames_pool(&frames, ctx->av_device_ref,
hw_imgfmt, imgfmt, 128, 128, false))
{
- MP_WARN(u->f, "failed to allocate pool\n");
+ MP_WARN(f, "failed to allocate pool\n");
continue;
}
@@ -375,9 +369,9 @@ static bool probe_formats(struct mp_hwupload *u, int hw_imgfmt)
int fmt = pixfmt2imgfmt(fmts[i]);
if (!fmt)
continue;
- MP_VERBOSE(u->f, " supports %s\n", mp_imgfmt_to_name(fmt));
+ MP_VERBOSE(f, " supports %s\n", mp_imgfmt_to_name(fmt));
if (!vo_supports(ctx, hw_imgfmt, fmt)) {
- MP_VERBOSE(u->f, " ... not supported by VO\n");
+ MP_VERBOSE(f, " ... not supported by VO\n");
continue;
}
MP_TARRAY_APPEND(p, p->upload_fmts, p->num_upload_fmts, fmt);
@@ -396,32 +390,78 @@ static bool probe_formats(struct mp_hwupload *u, int hw_imgfmt)
p->av_device_ctx = av_buffer_ref(ctx->av_device_ref);
if (!p->av_device_ctx)
return false;
+ p->conversion_filter_name = ctx->conversion_filter_name;
return p->num_upload_fmts > 0;
}
-struct mp_hwupload *mp_hwupload_create(struct mp_filter *parent, int hw_imgfmt)
+struct mp_hwupload mp_hwupload_create(struct mp_filter *parent, int hw_imgfmt,
+ int sw_imgfmt, bool src_is_same_hw)
{
+ struct mp_hwupload u = {0,};
struct mp_filter *f = mp_filter_create(parent, &hwupload_filter);
if (!f)
- return NULL;
+ return u;
struct priv *p = f->priv;
- struct mp_hwupload *u = &p->public;
- u->f = f;
-
mp_filter_add_pin(f, MP_PIN_IN, "in");
mp_filter_add_pin(f, MP_PIN_OUT, "out");
- if (!probe_formats(u, hw_imgfmt)) {
+ if (!probe_formats(f, hw_imgfmt)) {
MP_INFO(f, "hardware format not supported\n");
goto fail;
}
+ int hw_input_fmt = 0, hw_output_fmt = 0;
+ if (!select_format(p, sw_imgfmt, &hw_input_fmt, &hw_output_fmt)) {
+ MP_ERR(f, "Unable to find a compatible upload format for %s\n",
+ mp_imgfmt_to_name(sw_imgfmt));
+ goto fail;
+ }
+
+ if (src_is_same_hw) {
+ if (p->conversion_filter_name) {
+ /*
+ * If we are converting from one sw format to another within the same
+ * hw format, we will use that hw format's conversion filter rather
+ * than the actual hwupload filter.
+ */
+ u.selected_sw_imgfmt = hw_output_fmt;
+ if (sw_imgfmt != u.selected_sw_imgfmt) {
+ enum AVPixelFormat pixfmt = imgfmt2pixfmt(u.selected_sw_imgfmt);
+ const char *avfmt_name = av_get_pix_fmt_name(pixfmt);
+ char *args[] = {"format", (char *)avfmt_name, NULL};
+ MP_VERBOSE(f, "Hardware conversion: %s -> %s\n",
+ p->conversion_filter_name, avfmt_name);
+ struct mp_filter *sv =
+ mp_create_user_filter(parent, MP_OUTPUT_CHAIN_VIDEO,
+ p->conversion_filter_name, args);
+ u.f = sv;
+ talloc_free(f);
+ }
+ }
+ } else {
+ u.f = f;
+ /*
+ * In the case where the imgfmt is not natively supported, it must be
+ * converted, either before or during upload. If the imgfmt is supported
+ * as a hw input format, then prefer that, and if the upload has to do
+ * implicit conversion, that's fine. On the other hand, if the imgfmt is
+ * not a supported input format, then pick the output format as the
+ * conversion target to avoid doing two conversions (one before upload,
+ * and one during upload). Note that for most hardware types, there is
+ * no ability to convert during upload, and the two formats will always
+ * be the same.
+ */
+ u.selected_sw_imgfmt =
+ sw_imgfmt == hw_input_fmt ? hw_input_fmt : hw_output_fmt;
+ }
+
+ u.successful_init = true;
return u;
fail:
talloc_free(f);
- return NULL;
+ return u;
}
static void hwdownload_process(struct mp_filter *f)
diff --git a/filters/f_hwtransfer.h b/filters/f_hwtransfer.h
index 4f6c253193..dde9cf7137 100644
--- a/filters/f_hwtransfer.h
+++ b/filters/f_hwtransfer.h
@@ -4,16 +4,19 @@
// A filter which uploads sw frames to hw. Ignores hw frames.
struct mp_hwupload {
+ // Indicates if the filter was successfully initialised, or not.
+ // If not, the state of other members is undefined.
+ bool successful_init;
+
+ // The filter to use for uploads. NULL if none is required.
struct mp_filter *f;
-};
-struct mp_hwupload *mp_hwupload_create(struct mp_filter *parent, int hw_imgfmt);
+ // The underlying format of uploaded frames
+ int selected_sw_imgfmt;
+};
-// Return the best format suited for upload that is supported for a given input
-// imgfmt. This returns the same as imgfmt if the format is natively supported,
-// and otherwise a format that likely results in the least loss.
-// Returns 0 if completely unsupported.
-int mp_hwupload_find_upload_format(struct mp_hwupload *u, int imgfmt);
+struct mp_hwupload mp_hwupload_create(struct mp_filter *parent, int hw_imgfmt,
+ int sw_imgfmt, bool src_is_same_hw);
// A filter which downloads sw frames from hw. Ignores sw frames.
struct mp_hwdownload {
diff --git a/video/hwdec.h b/video/hwdec.h
index f3f2df06f2..44008dece0 100644
--- a/video/hwdec.h
+++ b/video/hwdec.h
@@ -18,6 +18,11 @@ struct mp_hwdec_ctx {
const int *supported_formats;
// HW format used by the hwdec
int hw_imgfmt;
+
+ // The name of this hwdec's matching conversion filter if available.
+ // This will be used for hardware conversion of frame formats.
+ // NULL otherwise.
+ const char *conversion_filter_name;
};
// Used to communicate hardware decoder device handles from VO to video decoder.
diff --git a/video/out/hwdec/hwdec_vaapi.c b/video/out/hwdec/hwdec_vaapi.c
index 9dd1fcd79e..fafc331277 100644
--- a/video/out/hwdec/hwdec_vaapi.c
+++ b/video/out/hwdec/hwdec_vaapi.c
@@ -131,6 +131,8 @@ const static dmabuf_interop_init interop_inits[] = {
NULL
};
+const static char *conversion_filter_name = "scale_vaapi";
+
static int init(struct ra_hwdec *hw)
{
struct priv_owner *p = hw->priv;
@@ -177,6 +179,7 @@ static int init(struct ra_hwdec *hw)
p->ctx->hwctx.hw_imgfmt = IMGFMT_VAAPI;
p->ctx->hwctx.supported_formats = p->formats;
p->ctx->hwctx.driver_name = hw->driver->name;
+ p->ctx->hwctx.conversion_filter_name = conversion_filter_name;
hwdec_devices_add(hw->devs, &p->ctx->hwctx);
return 0;
}