diff options
Diffstat (limited to 'filters')
-rw-r--r-- | filters/f_auto_filters.c | 244 | ||||
-rw-r--r-- | filters/f_auto_filters.h | 10 | ||||
-rw-r--r-- | filters/f_autoconvert.c | 288 | ||||
-rw-r--r-- | filters/f_autoconvert.h | 39 | ||||
-rw-r--r-- | filters/f_hwtransfer.c | 299 | ||||
-rw-r--r-- | filters/f_hwtransfer.h | 32 | ||||
-rw-r--r-- | filters/f_lavfi.c | 952 | ||||
-rw-r--r-- | filters/f_lavfi.h | 30 | ||||
-rw-r--r-- | filters/f_output_chain.c | 564 | ||||
-rw-r--r-- | filters/f_output_chain.h | 59 | ||||
-rw-r--r-- | filters/f_swscale.c | 148 | ||||
-rw-r--r-- | filters/f_swscale.h | 25 | ||||
-rw-r--r-- | filters/f_utils.c | 175 | ||||
-rw-r--r-- | filters/f_utils.h | 72 | ||||
-rw-r--r-- | filters/filter.c | 790 | ||||
-rw-r--r-- | filters/filter.h | 379 | ||||
-rw-r--r-- | filters/filter_internal.h | 144 | ||||
-rw-r--r-- | filters/frame.c | 179 | ||||
-rw-r--r-- | filters/frame.h | 55 | ||||
-rw-r--r-- | filters/user_filters.c | 119 | ||||
-rw-r--r-- | filters/user_filters.h | 29 |
21 files changed, 4632 insertions, 0 deletions
diff --git a/filters/f_auto_filters.c b/filters/f_auto_filters.c new file mode 100644 index 0000000000..eac6f745ca --- /dev/null +++ b/filters/f_auto_filters.c @@ -0,0 +1,244 @@ +#include <math.h> + +#include "common/common.h" +#include "common/msg.h" +#include "options/m_config.h" +#include "options/options.h" +#include "video/mp_image.h" + +#include "f_auto_filters.h" +#include "f_swscale.h" +#include "f_utils.h" +#include "filter.h" +#include "filter_internal.h" +#include "user_filters.h" + +struct deint_priv { + struct mp_subfilter sub; + int prev_imgfmt; + int prev_setting; + struct m_config_cache *opts; +}; + +static void deint_process(struct mp_filter *f) +{ + struct deint_priv *p = f->priv; + + if (!mp_subfilter_read(&p->sub)) + return; + + struct mp_frame frame = p->sub.frame; + + if (mp_frame_is_signaling(frame)) { + mp_subfilter_continue(&p->sub); + return; + } + + if (frame.type != MP_FRAME_VIDEO) { + MP_ERR(f, "video input required!\n"); + mp_filter_internal_mark_failed(f); + return; + } + + m_config_cache_update(p->opts); + struct filter_opts *opts = p->opts->opts; + + if (!opts->deinterlace) + mp_subfilter_destroy(&p->sub); + + struct mp_image *img = frame.data; + + if (img->imgfmt == p->prev_imgfmt && p->prev_setting == opts->deinterlace) { + mp_subfilter_continue(&p->sub); + return; + } + + if (!mp_subfilter_drain_destroy(&p->sub)) + return; + + assert(!p->sub.filter); + + p->prev_imgfmt = img->imgfmt; + p->prev_setting = opts->deinterlace; + if (!p->prev_setting) { + mp_subfilter_continue(&p->sub); + return; + } + + if (img->imgfmt == IMGFMT_VDPAU) { + char *args[] = {"deint", "yes", NULL}; + p->sub.filter = + mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "vdpaupp", args); + } else if (img->imgfmt == IMGFMT_VAAPI) { + p->sub.filter = + mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "vavpp", NULL); + } else if (img->imgfmt == IMGFMT_D3D11) { + p->sub.filter = + mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "d3d11vpp", NULL); + } else if (mp_sws_supports_input(img->imgfmt)) { + char *args[] = {"mode", "send_field", "deint", "interlaced", NULL}; + p->sub.filter = + mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "yadif", args); + } else { + MP_ERR(f, "no deinterlace filter available for this format\n"); + mp_subfilter_continue(&p->sub); + return; + } + + if (!p->sub.filter) + MP_ERR(f, "creating deinterlacer failed\n"); + + mp_subfilter_continue(&p->sub); +} + +static void deint_reset(struct mp_filter *f) +{ + struct deint_priv *p = f->priv; + + mp_subfilter_reset(&p->sub); +} + +static void deint_destroy(struct mp_filter *f) +{ + struct deint_priv *p = f->priv; + + mp_subfilter_reset(&p->sub); + TA_FREEP(&p->sub.filter); +} + +static const struct mp_filter_info deint_filter = { + .name = "deint", + .priv_size = sizeof(struct deint_priv), + .process = deint_process, + .reset = deint_reset, + .destroy = deint_destroy, +}; + +struct mp_filter *mp_deint_create(struct mp_filter *parent) +{ + struct mp_filter *f = mp_filter_create(parent, &deint_filter); + if (!f) + return NULL; + + struct deint_priv *p = f->priv; + + p->sub.in = mp_filter_add_pin(f, MP_PIN_IN, "in"); + p->sub.out = mp_filter_add_pin(f, MP_PIN_OUT, "out"); + + p->opts = m_config_cache_alloc(f, f->global, &filter_conf); + + return f; +} + +struct rotate_priv { + struct mp_subfilter sub; + int prev_rotate; + int prev_imgfmt; + int target_rotate; +}; + +static void rotate_process(struct mp_filter *f) +{ + struct rotate_priv *p = f->priv; + + if (!mp_subfilter_read(&p->sub)) + return; + + struct mp_frame frame = p->sub.frame; + + if (mp_frame_is_signaling(frame)) { + mp_subfilter_continue(&p->sub); + return; + } + + if (frame.type != MP_FRAME_VIDEO) { + MP_ERR(f, "video input required!\n"); + return; + } + + struct mp_image *img = frame.data; + + if (img->params.rotate == p->prev_rotate && + img->imgfmt == p->prev_imgfmt) + { + img->params.rotate = p->target_rotate; + mp_subfilter_continue(&p->sub); + return; + } + + if (!mp_subfilter_drain_destroy(&p->sub)) + return; + + assert(!p->sub.filter); + + int rotate = p->prev_rotate = img->params.rotate; + p->target_rotate = rotate; + p->prev_imgfmt = img->imgfmt; + + struct mp_stream_info *info = mp_filter_find_stream_info(f); + if (rotate == 0 || (info && info->rotate90 && !(rotate % 90))) { + mp_subfilter_continue(&p->sub); + return; + } + + if (mp_sws_supports_input(img->imgfmt)) { + MP_ERR(f, "Video rotation with this format not supported\n"); + mp_subfilter_continue(&p->sub); + return; + } + + double angle = rotate / 360.0 * M_PI * 2; + char *args[] = {"angle", mp_tprintf(30, "%f", angle), + "ow", mp_tprintf(30, "rotw(%f)", angle), + "oh", mp_tprintf(30, "roth(%f)", angle), + NULL}; + p->sub.filter = + mp_create_user_filter(f, MP_OUTPUT_CHAIN_VIDEO, "rotate", args); + + if (p->sub.filter) { + MP_INFO(f, "Inserting rotation filter.\n"); + p->target_rotate = 0; + } else { + MP_ERR(f, "could not create rotation filter\n"); + } + + mp_subfilter_continue(&p->sub); +} + +static void rotate_reset(struct mp_filter *f) +{ + struct rotate_priv *p = f->priv; + + mp_subfilter_reset(&p->sub); +} + +static void rotate_destroy(struct mp_filter *f) +{ + struct rotate_priv *p = f->priv; + + mp_subfilter_reset(&p->sub); + TA_FREEP(&p->sub.filter); +} + +static const struct mp_filter_info rotate_filter = { + .name = "autorotate", + .priv_size = sizeof(struct rotate_priv), + .process = rotate_process, + .reset = rotate_reset, + .destroy = rotate_destroy, +}; + +struct mp_filter *mp_autorotate_create(struct mp_filter *parent) +{ + struct mp_filter *f = mp_filter_create(parent, &rotate_filter); + if (!f) + return NULL; + + struct rotate_priv *p = f->priv; + p->prev_rotate = -1; + + p->sub.in = mp_filter_add_pin(f, MP_PIN_IN, "in"); + p->sub.out = mp_filter_add_pin(f, MP_PIN_OUT, "out"); + + return f; +} diff --git a/filters/f_auto_filters.h b/filters/f_auto_filters.h new file mode 100644 index 0000000000..5f1a99f636 --- /dev/null +++ b/filters/f_auto_filters.h @@ -0,0 +1,10 @@ +#pragma once + +#include "filter.h" + +// A filter which inserts the required deinterlacing filter based on the +// hardware decode mode and the deinterlace user option. +struct mp_filter *mp_deint_create(struct mp_filter *parent); + +// Rotate according to mp_image.rotate and VO capabilities. +struct mp_filter *mp_autorotate_create(struct mp_filter *parent); diff --git a/filters/f_autoconvert.c b/filters/f_autoconvert.c new file mode 100644 index 0000000000..687a846ae5 --- /dev/null +++ b/filters/f_autoconvert.c @@ -0,0 +1,288 @@ +#include "config.h" + +#include "common/common.h" +#include "common/msg.h" +#include "video/hwdec.h" +#include "video/mp_image.h" + +#include "f_autoconvert.h" +#include "f_hwtransfer.h" +#include "f_swscale.h" +#include "f_utils.h" +#include "filter.h" +#include "filter_internal.h" + +struct priv { + struct mp_log *log; + + struct mp_subfilter sub; + + bool force_update; + + int *imgfmts; + int *subfmts; + int num_imgfmts; + + // Enable special conversion for the final stage before the VO. + bool vo_convert; + + // sws state + int in_imgfmt, in_subfmt; + + struct mp_autoconvert public; +}; + +// Dummy filter for bundling sub-conversion filters. +static const struct mp_filter_info convert_filter = { + .name = "convert", +}; + +// For hw decoding: thing which can convert between underlying surface formats. +// The filter detects the needed target format from struct mp_hwdec_ctx. +struct subfmt_conv { + int hw_imgfmt; + struct mp_filter *(*create)(struct mp_filter *parent); +}; + +static const struct subfmt_conv subfmt_converters[] = { +#if HAVE_D3D_HWACCEL + {IMGFMT_D3D11, vf_d3d11_create_outconv}, +#endif + {0} +}; + +void mp_autoconvert_clear(struct mp_autoconvert *c) +{ + struct priv *p = c->f->priv; + + p->num_imgfmts = 0; +} + +void mp_autoconvert_add_imgfmt(struct mp_autoconvert *c, int imgfmt, int subfmt) +{ + struct priv *p = c->f->priv; + + MP_TARRAY_GROW(p, p->imgfmts, p->num_imgfmts); + MP_TARRAY_GROW(p, p->subfmts, p->num_imgfmts); + + p->imgfmts[p->num_imgfmts] = imgfmt; + p->subfmts[p->num_imgfmts] = subfmt; + + p->num_imgfmts += 1; + p->force_update = true; +} + +void mp_autoconvert_add_vo_hwdec_subfmts(struct mp_autoconvert *c, + struct mp_hwdec_devices *devs) +{ + struct priv *p = c->f->priv; + assert(devs); + + int prev_format = 0; + + for (int n = 0; ; n++) { + struct mp_hwdec_ctx *ctx = hwdec_devices_get_n(devs, n); + if (!ctx) + break; + if (!ctx->hw_imgfmt || !ctx->supported_formats) + continue; + // Very hacky: don't let d3d11-egl-rgb overwrite d3d11-egl + if (ctx->hw_imgfmt == prev_format) + continue; + prev_format = ctx->hw_imgfmt; + // Stupidity: VOs export imgfmt only, so subfmt is always 0. Remove it + // to fix it up. + for (int i = 0; i < p->num_imgfmts; i++) { + if (p->imgfmts[i] != ctx->hw_imgfmt) + continue; + + int count = p->num_imgfmts; + MP_TARRAY_REMOVE_AT(p->imgfmts, count, i); + count = p->num_imgfmts; + MP_TARRAY_REMOVE_AT(p->subfmts, count, i); + p->num_imgfmts -= 1; + break; + } + for (int i = 0; ctx->supported_formats[i]; i++) + mp_autoconvert_add_imgfmt(c, ctx->hw_imgfmt, ctx->supported_formats[i]); + } + + p->vo_convert = true; +} + +static void handle_video_frame(struct mp_filter *f) +{ + struct priv *p = f->priv; + + struct mp_frame frame = p->sub.frame; + if (frame.type != MP_FRAME_VIDEO) { + MP_ERR(p, "video input required!\n"); + mp_filter_internal_mark_failed(f); + return; + } + + struct mp_image *img = frame.data; + + if (p->force_update) + p->in_imgfmt = p->in_subfmt = 0; + + if (img->imgfmt == p->in_imgfmt && img->params.hw_subfmt == p->in_subfmt) { + mp_subfilter_continue(&p->sub); + return; + } + + if (!mp_subfilter_drain_destroy(&p->sub)) { + p->in_imgfmt = p->in_subfmt = 0; + return; + } + + p->in_imgfmt = img->params.imgfmt; + p->in_subfmt = img->params.hw_subfmt; + p->force_update = false; + + bool different_subfmt = false; + + 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) + different_subfmt = true; + if (samefmt && (samesubffmt || !p->subfmts[n])) { + mp_subfilter_continue(&p->sub); + return; + } + } + + struct mp_stream_info *info = mp_filter_find_stream_info(f); + + struct mp_filter *conv = mp_filter_create(f, &convert_filter); + mp_filter_add_pin(conv, MP_PIN_IN, "in"); + mp_filter_add_pin(conv, MP_PIN_OUT, "out"); + + struct mp_filter *filters[2] = {0}; + bool need_sws = true; + + int *fmts = p->imgfmts; + int num_fmts = p->num_imgfmts; + + // 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]); + + 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; + fmts = upload->upload_fmts; + num_fmts = upload->num_upload_fmts; + } + } 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]) { + need_sws = false; + break; + } + } + } + } + + if (need_sws) { + // Create a new conversion filter. + struct mp_sws_filter *sws = mp_sws_filter_create(conv); + if (!sws) { + MP_ERR(p, "error creating conversion filter\n"); + return; + } + + int out = mp_sws_find_best_out_format(img->imgfmt, 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; + } + + if (out == img->imgfmt) { + // 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_imgfmt_to_name(sws->out_format)); + filters[0] = sws->f; + } + } + + mp_chain_filters(conv->ppins[0], conv->ppins[1], filters, 2); + + p->sub.filter = conv; + mp_subfilter_continue(&p->sub); +} + +static void process(struct mp_filter *f) +{ + struct priv *p = f->priv; + + if (!mp_subfilter_read(&p->sub)) + return; + + struct mp_frame frame = p->sub.frame; + + if (!mp_frame_is_signaling(frame)) { + if (p->num_imgfmts) { + handle_video_frame(f); + return; + } + } + + mp_subfilter_continue(&p->sub); +} + +static void reset(struct mp_filter *f) +{ + struct priv *p = f->priv; + + mp_subfilter_reset(&p->sub); +} + +static void destroy(struct mp_filter *f) +{ + struct priv *p = f->priv; + + mp_subfilter_reset(&p->sub); + TA_FREEP(&p->sub.filter); +} + +static const struct mp_filter_info autoconvert_filter = { + .name = "autoconvert", + .priv_size = sizeof(struct priv), + .process = process, + .reset = reset, + .destroy = destroy, +}; + +struct mp_autoconvert *mp_autoconvert_create(struct mp_filter *parent) +{ + struct mp_filter *f = mp_filter_create(parent, &autoconvert_filter); + if (!f) + return NULL; + + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); + + struct priv *p = f->priv; + p->public.f = f; + p->log = f->log; + p->sub.in = f->ppins[0]; + p->sub.out = f->ppins[1]; + + return &p->public; +} diff --git a/filters/f_autoconvert.h b/filters/f_autoconvert.h new file mode 100644 index 0000000000..72af21a0df --- /dev/null +++ b/filters/f_autoconvert.h @@ -0,0 +1,39 @@ +#pragma once + +#include "filter.h" + +// A filter which automatically creates and uses a conversion filter based on +// the filter settings, or passes through data unchanged if no conversion is +// required. +struct mp_autoconvert { + // f->pins[0] is input, f->pins[1] is output + struct mp_filter *f; +}; + +// (to free this, free the filter itself, mp_autoconvert.f) +struct mp_autoconvert *mp_autoconvert_create(struct mp_filter *parent); + +// Add the imgfmt as allowed video image format, and error on non-video frames. +// Each call adds to the list of allowed formats. Before the first call, all +// formats are allowed (even non-video). +// subfmt can be used to specify underlying surface formats for hardware formats, +// otherwise must be 0. +void mp_autoconvert_add_imgfmt(struct mp_autoconvert *c, int imgfmt, int subfmt); + +// 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). +// Must be called mp_autoconvert_add_imgfmt(), and overrides them where formats +// collide. +struct mp_hwdec_devices; +void mp_autoconvert_add_vo_hwdec_subfmts(struct mp_autoconvert *c, + struct mp_hwdec_devices *devs); + +// Reset set of allowed formats back to initial state. (This does not flush +// any frames or remove currently active filters, although to get reasonable +// behavior, you need to readd all previously allowed formats, or reset the +// filter.) +void mp_autoconvert_clear(struct mp_autoconvert *c); + +// vf_d3d11vpp.c +struct mp_filter *vf_d3d11_create_outconv(struct mp_filter *parent); diff --git a/filters/f_hwtransfer.c b/filters/f_hwtransfer.c new file mode 100644 index 0000000000..6ffda567ae --- /dev/null +++ b/filters/f_hwtransfer.c @@ -0,0 +1,299 @@ +#include <libavutil/buffer.h> +#include <libavutil/hwcontext.h> +#include <libavutil/mem.h> + +#include "video/fmt-conversion.h" +#include "video/hwdec.h" +#include "video/mp_image.h" +#include "video/mp_image_pool.h" + +#include "f_hwtransfer.h" +#include "filter_internal.h" + +struct priv { + AVBufferRef *av_device_ctx; + + AVBufferRef *hw_pool; + + int last_input_fmt; + int last_upload_fmt; + int last_sw_fmt; + + struct mp_hwupload public; +}; + +static bool update_format_decision(struct priv *p, int input_fmt) +{ + struct mp_hwupload *u = &p->public; + + if (!input_fmt) + return false; + + if (input_fmt == p->last_input_fmt) + return true; + + p->last_input_fmt = 0; + + int res = mp_imgfmt_select_best_list(u->upload_fmts, u->num_upload_fmts, + input_fmt); + + if (!res) + return false; + + // Find which sw format we should use. + // NOTE: if there are ever any hw APIs that actually do expensive + // conversions on mismatching format uploads, we should probably first look + // which sw format is preferred? + int index = -1; + for (int n = 0; n < u->num_upload_fmts; n++) { + if (u->upload_fmts[n] == res) + index = n; + } + + if (index < 0) + return false; + + for (int n = 0; n < u->num_fmts; n++) { + if (u->fmt_upload_index[n] >= index && + index < u->fmt_upload_index[n] + u->fmt_upload_num[n]) + { + p->last_input_fmt = input_fmt; + p->last_upload_fmt = u->upload_fmts[index]; + p->last_sw_fmt = u->fmts[n]; + MP_INFO(u->f, "upload %s -> %s\n", + mp_imgfmt_to_name(p->last_sw_fmt), + mp_imgfmt_to_name(p->last_input_fmt)); + return true; + } + } + + return false; +} + +int mp_hwupload_find_upload_format(struct mp_hwupload *u, int imgfmt) +{ + struct priv *p = u->f->priv; + + if (!update_format_decision(p, imgfmt)) + return 0; + return p->last_upload_fmt; +} + +static void process(struct mp_filter *f) +{ + struct priv *p = f->priv; + + if (!mp_pin_can_transfer_data(f->ppins[1], f->ppins[0])) + return; + + struct mp_frame frame = mp_pin_out_read(f->ppins[0]); + if (mp_frame_is_signaling(frame)) { + mp_pin_in_write(f->ppins[1], frame); + return; + } + if (frame.type != MP_FRAME_VIDEO) { + MP_ERR(f, "unsupported frame type\n"); + goto error; + } + struct mp_image *src = frame.data; + + // As documented, just pass though HW frames. + if (IMGFMT_IS_HWACCEL(src->imgfmt)) { + mp_pin_in_write(f->ppins[1], frame); + return; + } + + if (src->w % 2 || src->h % 2) { + MP_ERR(f, "non-mod 2 input frames unsupported\n"); + goto error; + } + + if (!update_format_decision(p, src->imgfmt)) { + MP_ERR(f, "no hw upload format found\n"); + goto error; + } + + if (!mp_update_av_hw_frames_pool(&p->hw_pool, p->av_device_ctx, + p->public.hw_imgfmt, p->last_sw_fmt, + src->w, src->h)) + { + MP_ERR(f, "failed to create frame pool\n"); + goto error; + } + + struct mp_image *dst = mp_av_pool_image_hw_upload(p->hw_pool, src); + if (!dst) + goto error; + + mp_frame_unref(&frame); + mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_VIDEO, dst)); + + return; + +error: + mp_frame_unref(&frame); + MP_ERR(f, "failed to upload frame\n"); + mp_filter_internal_mark_failed(f); +} + +static void destroy(struct mp_filter *f) +{ + struct priv *p = f->priv; + + av_buffer_unref(&p->hw_pool); + av_buffer_unref(&p->av_device_ctx); +} + +static const struct mp_filter_info hwupload_filter = { + .name = "hwupload", + .priv_size = sizeof(struct priv), + .process = process, + .destroy = destroy, +}; + +// The VO layer might have restricted format support. It might actually +// work if this is input to a conversion filter anyway, but our format +// negotiation is too stupid and non-existent to detect this. +// So filter out all not explicitly supported formats. +static bool vo_supports(struct mp_hwdec_ctx *ctx, int hw_fmt, int sw_fmt) +{ + if (!ctx->hw_imgfmt) + return true; // if unset, all formats are allowed + if (ctx->hw_imgfmt != hw_fmt) + return false; + + for (int i = 0; ctx->supported_formats && ctx->supported_formats[i]; i++) { + if (ctx->supported_formats[i] == sw_fmt) + return true; + } + + return false; +} + +static bool probe_formats(struct mp_hwupload *u, int hw_imgfmt) +{ + struct priv *p = u->f->priv; + + u->hw_imgfmt = hw_imgfmt; + u->num_fmts = 0; + u->num_upload_fmts = 0; + + struct mp_stream_info *info = mp_filter_find_stream_info(u->f); + if (!info || !info->hwdec_devs) { + MP_ERR(u->f, "no hw context\n"); + return false; + } + + struct mp_hwdec_ctx *ctx = NULL; + AVHWFramesConstraints *cstr = NULL; + + for (int n = 0; ; n++) { + struct mp_hwdec_ctx *cur = hwdec_devices_get_n(info->hwdec_devs, n); + if (!cur) + break; + if (!cur->av_device_ref) + continue; + cstr = av_hwdevice_get_hwframe_constraints(cur->av_device_ref, NULL); + if (!cstr) + continue; + bool found = false; + for (int i = 0; cstr->valid_hw_formats && + cstr->valid_hw_formats[i] != AV_PIX_FMT_NONE; i++) + { + found |= cstr->valid_hw_formats[i] == imgfmt2pixfmt(hw_imgfmt); + } + if (found && (!cur->hw_imgfmt || cur->hw_imgfmt == hw_imgfmt)) { + ctx = cur; + break; + } + av_hwframe_constraints_free(&cstr); + } + + if (!ctx) { + MP_ERR(u->f, "no support for this hw format\n"); + return false; + } + + // Probe for supported formats. This is very roundabout, because the + // hwcontext API does not give us this information directly. We resort to + // creating temporary AVHWFramesContexts in order to retrieve the list of + // supported formats. This should be relatively cheap as we don't create + // any real frames (although some backends do for probing info). + + for (int n = 0; cstr->valid_sw_formats && + cstr->valid_sw_formats[n] != AV_PIX_FMT_NONE; n++) + { + int imgfmt = pixfmt2imgfmt(cstr->valid_sw_formats[n]); + if (!imgfmt) + continue; + + MP_VERBOSE(u->f, "looking at format %s\n", mp_imgfmt_to_name(imgfmt)); + + // Creates an AVHWFramesContexts with the given parameters. + AVBufferRef *frames = NULL; + if (!mp_update_av_hw_frames_pool(&frames, ctx->av_device_ref, + hw_imgfmt, imgfmt, 128, 128)) + { + MP_WARN(u->f, "failed to allocate pool\n"); + continue; + } + + enum AVPixelFormat *fmts; + if (av_hwframe_transfer_get_formats(frames, + AV_HWFRAME_TRANSFER_DIRECTION_TO, &fmts, 0) >= 0) + { + int index = u->num_fmts; + MP_TARRAY_APPEND(p, u->fmts, u->num_fmts, imgfmt); + MP_TARRAY_GROW(p, u->fmt_upload_index, index); + MP_TARRAY_GROW(p, u->fmt_upload_num, index); + + u->fmt_upload_index[index] = u->num_upload_fmts; + + for (int i = 0; fmts[i] != AV_PIX_FMT_NONE; i++) { + int fmt = pixfmt2imgfmt(fmts[i]); + if (!fmt) + continue; + MP_VERBOSE(u->f, "supports %s\n", mp_imgfmt_to_name(fmt)); + if (vo_supports(ctx, hw_imgfmt, fmt)) + MP_TARRAY_APPEND(p, u->upload_fmts, u->num_upload_fmts, fmt); + } + + u->fmt_upload_num[index] = + u->num_upload_fmts - u->fmt_upload_index[index]; + + av_free(fmts); + } + + av_buffer_unref(&frames); + } + + p->av_device_ctx = av_buffer_ref(ctx->av_device_ref); + if (!p->av_device_ctx) + return false; + + return u->num_upload_fmts > 0; +} + +struct mp_hwupload *mp_hwupload_create(struct mp_filter *parent, int hw_imgfmt) +{ + struct mp_filter *f = mp_filter_create(parent, &hwupload_filter); + if (!f) + return NULL; + + 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)) { + MP_ERR(f, "hardware format not supported\n"); + goto error; + } + + return u; +error: + talloc_free(f); + return NULL; +} diff --git a/filters/f_hwtransfer.h b/filters/f_hwtransfer.h new file mode 100644 index 0000000000..4595cb393d --- /dev/null +++ b/filters/f_hwtransfer.h @@ -0,0 +1,32 @@ +#pragma once + +#include "filter.h" + +// A filter which uploads sw frames to hw. Ignores hw frames. +struct mp_hwupload { + struct mp_filter *f; + + // Hardware wrapper format, e.g. IMGFMT_VAAPI. + int hw_imgfmt; + + // List of supported underlying surface formats. + int *fmts; + int num_fmts; + // List of supported upload image formats. May contain duplicate entries + // (which should be ignored). + int *upload_fmts; |