video: rewrite filtering glue code
Get rid of the old vf.c code. Replace it with a generic filtering
framework, which can potentially handle more than just --vf. At least
reimplementing --af with this code is planned.
This changes some --vf semantics (including runtime behavior and the
"vf" command). The most important ones are listed in interface-changes.
vf_convert.c is renamed to f_swscale.c. It is now an internal filter
that can not be inserted by the user manually.
f_lavfi.c is a refactor of player/lavfi.c. The latter will be removed
once --lavfi-complex is reimplemented on top of f_lavfi.c. (which is
conceptually easy, but a big mess due to the data flow changes).
The existing filters are all changed heavily. The data flow of the new
filter framework is different. Especially EOF handling changes - EOF is
now a "frame" rather than a state, and must be passed through exactly
once.
Another major thing is that all filters must support dynamic format
changes. The filter reconfig() function goes away. (This sounds complex,
but since all filters need to handle EOF draining anyway, they can use
the same code, and it removes the mess with reconfig() having to predict
the output format, which completely breaks with libavfilter anyway.)
In addition, there is no automatic format negotiation or conversion.
libavfilter's primitive and insufficient API simply doesn't allow us to
do this in a reasonable way. Instead, filters can use f_autoconvert as
sub-filter, and tell it which formats they support. This filter will in
turn add actual conversion filters, such as f_swscale, to perform
necessary format changes.
vf_vapoursynth.c uses the same basic principle of operation as before,
but with worryingly different details in data flow. Still appears to
work.
The hardware deint filters (vf_vavpp.c, vf_d3d11vpp.c, vf_vdpaupp.c) are
heavily changed. Fortunately, they all used refqueue.c, which is for
sharing the data flow logic (especially for managing future/past
surfaces and such). It turns out it can be used to factor out most of
the data flow. Some of these filters accepted software input. Instead of
having ad-hoc upload code in each filter, surface upload is now
delegated to f_autoconvert, which can use f_hwupload to perform this.
Exporting VO capabilities is still a big mess (mp_stream_info stuff).
The D3D11 code drops the redundant image formats, and all code uses the
hw_subfmt (sw_format in FFmpeg) instead. Although that too seems to be a
big mess for now.
f_async_queue is unused.
2018-01-16 10:53:44 +00:00
|
|
|
#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;
|
|
|
|
|
f_hwtransfer: change order in which hwdec upload formats are considered
Basically, instead of trusting the upload format, and picking the first
sw format that has a desired upload format, trust the sw format. So now
we pick the sw format first, and then select from the set of upload
formats supported by it.
This is probably more straightforward, and works around a crash with
vaapi:
mpv 8bit.mkv --vf=format=vaapi --gpu-hwdec-interop=all
(Forces vaapi upload if hw decoding is not enabled.)
Unfortunately, this still does not work, because vaapi, FFmpeg, the VO
interop code, or all of them are doing something stupid. In particular,
this picks the yuv420p sw format, which doesn't really exist despiter
advertised (???????????????????????????????????????), and simply breaks.
See: #7350
2020-01-11 21:28:47 +00:00
|
|
|
// First find the closest sw fmt. Some hwdec APIs return crazy lists of
|
|
|
|
// "supported" formats, which then are not supported or crash (???), so
|
|
|
|
// the this is a good way to avoid problems.
|
|
|
|
// (Actually we should just have hardcoded everything instead of relying on
|
|
|
|
// this fragile bullshit FFmpeg API and the fragile bullshit hwdec drivers.)
|
|
|
|
int sw_fmt = mp_imgfmt_select_best_list(u->fmts, u->num_fmts, input_fmt);
|
|
|
|
if (!sw_fmt)
|
video: rewrite filtering glue code
Get rid of the old vf.c code. Replace it with a generic filtering
framework, which can potentially handle more than just --vf. At least
reimplementing --af with this code is planned.
This changes some --vf semantics (including runtime behavior and the
"vf" command). The most important ones are listed in interface-changes.
vf_convert.c is renamed to f_swscale.c. It is now an internal filter
that can not be inserted by the user manually.
f_lavfi.c is a refactor of player/lavfi.c. The latter will be removed
once --lavfi-complex is reimplemented on top of f_lavfi.c. (which is
conceptually easy, but a big mess due to the data flow changes).
The existing filters are all changed heavily. The data flow of the new
filter framework is different. Especially EOF handling changes - EOF is
now a "frame" rather than a state, and must be passed through exactly
once.
Another major thing is that all filters must support dynamic format
changes. The filter reconfig() function goes away. (This sounds complex,
but since all filters need to handle EOF draining anyway, they can use
the same code, and it removes the mess with reconfig() having to predict
the output format, which completely breaks with libavfilter anyway.)
In addition, there is no automatic format negotiation or conversion.
libavfilter's primitive and insufficient API simply doesn't allow us to
do this in a reasonable way. Instead, filters can use f_autoconvert as
sub-filter, and tell it which formats they support. This filter will in
turn add actual conversion filters, such as f_swscale, to perform
necessary format changes.
vf_vapoursynth.c uses the same basic principle of operation as before,
but with worryingly different details in data flow. Still appears to
work.
The hardware deint filters (vf_vavpp.c, vf_d3d11vpp.c, vf_vdpaupp.c) are
heavily changed. Fortunately, they all used refqueue.c, which is for
sharing the data flow logic (especially for managing future/past
surfaces and such). It turns out it can be used to factor out most of
the data flow. Some of these filters accepted software input. Instead of
having ad-hoc upload code in each filter, surface upload is now
delegated to f_autoconvert, which can use f_hwupload to perform this.
Exporting VO capabilities is still a big mess (mp_stream_info stuff).
The D3D11 code drops the redundant image formats, and all code uses the
hw_subfmt (sw_format in FFmpeg) instead. Although that too seems to be a
big mess for now.
f_async_queue is unused.
2018-01-16 10:53:44 +00:00
|
|
|
return false;
|
|
|
|
|
f_hwtransfer: change order in which hwdec upload formats are considered
Basically, instead of trusting the upload format, and picking the first
sw format that has a desired upload format, trust the sw format. So now
we pick the sw format first, and then select from the set of upload
formats supported by it.
This is probably more straightforward, and works around a crash with
vaapi:
mpv 8bit.mkv --vf=format=vaapi --gpu-hwdec-interop=all
(Forces vaapi upload if hw decoding is not enabled.)
Unfortunately, this still does not work, because vaapi, FFmpeg, the VO
interop code, or all of them are doing something stupid. In particular,
this picks the yuv420p sw format, which doesn't really exist despiter
advertised (???????????????????????????????????????), and simply breaks.
See: #7350
2020-01-11 21:28:47 +00:00
|
|
|
// Dumb, but find index for u->fmts[index]==sw_fmt.
|
video: rewrite filtering glue code
Get rid of the old vf.c code. Replace it with a generic filtering
framework, which can potentially handle more than just --vf. At least
reimplementing --af with this code is planned.
This changes some --vf semantics (including runtime behavior and the
"vf" command). The most important ones are listed in interface-changes.
vf_convert.c is renamed to f_swscale.c. It is now an internal filter
that can not be inserted by the user manually.
f_lavfi.c is a refactor of player/lavfi.c. The latter will be removed
once --lavfi-complex is reimplemented on top of f_lavfi.c. (which is
conceptually easy, but a big mess due to the data flow changes).
The existing filters are all changed heavily. The data flow of the new
filter framework is different. Especially EOF handling changes - EOF is
now a "frame" rather than a state, and must be passed through exactly
once.
Another major thing is that all filters must support dynamic format
changes. The filter reconfig() function goes away. (This sounds complex,
but since all filters need to handle EOF draining anyway, they can use
the same code, and it removes the mess with reconfig() having to predict
the output format, which completely breaks with libavfilter anyway.)
In addition, there is no automatic format negotiation or conversion.
libavfilter's primitive and insufficient API simply doesn't allow us to
do this in a reasonable way. Instead, filters can use f_autoconvert as
sub-filter, and tell it which formats they support. This filter will in
turn add actual conversion filters, such as f_swscale, to perform
necessary format changes.
vf_vapoursynth.c uses the same basic principle of operation as before,
but with worryingly different details in data flow. Still appears to
work.
The hardware deint filters (vf_vavpp.c, vf_d3d11vpp.c, vf_vdpaupp.c) are
heavily changed. Fortunately, they all used refqueue.c, which is for
sharing the data flow logic (especially for managing future/past
surfaces and such). It turns out it can be used to factor out most of
the data flow. Some of these filters accepted software input. Instead of
having ad-hoc upload code in each filter, surface upload is now
delegated to f_autoconvert, which can use f_hwupload to perform this.
Exporting VO capabilities is still a big mess (mp_stream_info stuff).
The D3D11 code drops the redundant image formats, and all code uses the
hw_subfmt (sw_format in FFmpeg) instead. Although that too seems to be a
big mess for now.
f_async_queue is unused.
2018-01-16 10:53:44 +00:00
|
|
|
int index = -1;
|
f_hwtransfer: change order in which hwdec upload formats are considered
Basically, instead of trusting the upload format, and picking the first
sw format that has a desired upload format, trust the sw format. So now
we pick the sw format first, and then select from the set of upload
formats supported by it.
This is probably more straightforward, and works around a crash with
vaapi:
mpv 8bit.mkv --vf=format=vaapi --gpu-hwdec-interop=all
(Forces vaapi upload if hw decoding is not enabled.)
Unfortunately, this still does not work, because vaapi, FFmpeg, the VO
interop code, or all of them are doing something stupid. In particular,
this picks the yuv420p sw format, which doesn't really exist despiter
advertised (???????????????????????????????????????), and simply breaks.
See: #7350
2020-01-11 21:28:47 +00:00
|
|
|
for (int n = 0; n < u->num_fmts; n++) {
|
|
|
|
if (u->fmts[n] == sw_fmt)
|
video: rewrite filtering glue code
Get rid of the old vf.c code. Replace it with a generic filtering
framework, which can potentially handle more than just --vf. At least
reimplementing --af with this code is planned.
This changes some --vf semantics (including runtime behavior and the
"vf" command). The most important ones are listed in interface-changes.
vf_convert.c is renamed to f_swscale.c. It is now an internal filter
that can not be inserted by the user manually.
f_lavfi.c is a refactor of player/lavfi.c. The latter will be removed
once --lavfi-complex is reimplemented on top of f_lavfi.c. (which is
conceptually easy, but a big mess due to the data flow changes).
The existing filters are all changed heavily. The data flow of the new
filter framework is different. Especially EOF handling changes - EOF is
now a "frame" rather than a state, and must be passed through exactly
once.
Another major thing is that all filters must support dynamic format
changes. The filter reconfig() function goes away. (This sounds complex,
but since all filters need to handle EOF draining anyway, they can use
the same code, and it removes the mess with reconfig() having to predict
the output format, which completely breaks with libavfilter anyway.)
In addition, there is no automatic format negotiation or conversion.
libavfilter's primitive and insufficient API simply doesn't allow us to
do this in a reasonable way. Instead, filters can use f_autoconvert as
sub-filter, and tell it which formats they support. This filter will in
turn add actual conversion filters, such as f_swscale, to perform
necessary format changes.
vf_vapoursynth.c uses the same basic principle of operation as before,
but with worryingly different details in data flow. Still appears to
work.
The hardware deint filters (vf_vavpp.c, vf_d3d11vpp.c, vf_vdpaupp.c) are
heavily changed. Fortunately, they all used refqueue.c, which is for
sharing the data flow logic (especially for managing future/past
surfaces and such). It turns out it can be used to factor out most of
the data flow. Some of these filters accepted software input. Instead of
having ad-hoc upload code in each filter, surface upload is now
delegated to f_autoconvert, which can use f_hwupload to perform this.
Exporting VO capabilities is still a big mess (mp_stream_info stuff).
The D3D11 code drops the redundant image formats, and all code uses the
hw_subfmt (sw_format in FFmpeg) instead. Although that too seems to be a
big mess for now.
f_async_queue is unused.
2018-01-16 10:53:44 +00:00
|
|
|
index = n;
|
|
|
|
}
|
|
|
|
if (index < 0)
|
|
|
|
return false;
|
|
|
|
|
f_hwtransfer: change order in which hwdec upload formats are considered
Basically, instead of trusting the upload format, and picking the first
sw format that has a desired upload format, trust the sw format. So now
we pick the sw format first, and then select from the set of upload
formats supported by it.
This is probably more straightforward, and works around a crash with
vaapi:
mpv 8bit.mkv --vf=format=vaapi --gpu-hwdec-interop=all
(Forces vaapi upload if hw decoding is not enabled.)
Unfortunately, this still does not work, because vaapi, FFmpeg, the VO
interop code, or all of them are doing something stupid. In particular,
this picks the yuv420p sw format, which doesn't really exist despiter
advertised (???????????????????????????????????????), and simply breaks.
See: #7350
2020-01-11 21:28:47 +00:00
|
|
|
// Now check the available upload formats. This is the format our sw frame
|
|
|
|
// has to be in, and which the upload API will take (probably).
|
|
|
|
|
|
|
|
int *upload_fmts = &u->upload_fmts[u->fmt_upload_index[index]];
|
|
|
|
int num_upload_fmts = u->fmt_upload_num[index];
|
|
|
|
|
|
|
|
int up_fmt = mp_imgfmt_select_best_list(upload_fmts, num_upload_fmts,
|
|
|
|
input_fmt);
|
|
|
|
if (!up_fmt)
|
|
|
|
return false;
|
video: rewrite filtering glue code
Get rid of the old vf.c code. Replace it with a generic filtering
framework, which can potentially handle more than just --vf. At least
reimplementing --af with this code is planned.
This changes some --vf semantics (including runtime behavior and the
"vf" command). The most important ones are listed in interface-changes.
vf_convert.c is renamed to f_swscale.c. It is now an internal filter
that can not be inserted by the user manually.
f_lavfi.c is a refactor of player/lavfi.c. The latter will be removed
once --lavfi-complex is reimplemented on top of f_lavfi.c. (which is
conceptually easy, but a big mess due to the data flow changes).
The existing filters are all changed heavily. The data flow of the new
filter framework is different. Especially EOF handling changes - EOF is
now a "frame" rather than a state, and must be passed through exactly
once.
Another major thing is that all filters must support dynamic format
changes. The filter reconfig() function goes away. (This sounds complex,
but since all filters need to handle EOF draining anyway, they can use
the same code, and it removes the mess with reconfig() having to predict
the output format, which completely breaks with libavfilter anyway.)
In addition, there is no automatic format negotiation or conversion.
libavfilter's primitive and insufficient API simply doesn't allow us to
do this in a reasonable way. Instead, filters can use f_autoconvert as
sub-filter, and tell it which formats they support. This filter will in
turn add actual conversion filters, such as f_swscale, to perform
necessary format changes.
vf_vapoursynth.c uses the same basic principle of operation as before,
but with worryingly different details in data flow. Still appears to
work.
The hardware deint filters (vf_vavpp.c, vf_d3d11vpp.c, vf_vdpaupp.c) are
heavily changed. Fortunately, they all used refqueue.c, which is for
sharing the data flow logic (especially for managing future/past
surfaces and such). It turns out it can be used to factor out most of
the data flow. Some of these filters accepted software input. Instead of
having ad-hoc upload code in each filter, surface upload is now
delegated to f_autoconvert, which can use f_hwupload to perform this.
Exporting VO capabilities is still a big mess (mp_stream_info stuff).
The D3D11 code drops the redundant image formats, and all code uses the
hw_subfmt (sw_format in FFmpeg) instead. Although that too seems to be a
big mess for now.
f_async_queue is unused.
2018-01-16 10:53:44 +00:00
|
|
|
|
f_hwtransfer: change order in which hwdec upload formats are considered
Basically, instead of trusting the upload format, and picking the first
sw format that has a desired upload format, trust the sw format. So now
we pick the sw format first, and then select from the set of upload
formats supported by it.
This is probably more straightforward, and works around a crash with
vaapi:
mpv 8bit.mkv --vf=format=vaapi --gpu-hwdec-interop=all
(Forces vaapi upload if hw decoding is not enabled.)
Unfortunately, this still does not work, because vaapi, FFmpeg, the VO
interop code, or all of them are doing something stupid. In particular,
this picks the yuv420p sw format, which doesn't really exist despiter
advertised (???????????????????????????????????????), and simply breaks.
See: #7350
2020-01-11 21:28:47 +00:00
|
|
|
p->last_input_fmt = input_fmt;
|
|
|
|
p->last_upload_fmt = up_fmt;
|
|
|
|
p->last_sw_fmt = sw_fmt;
|
|
|
|
MP_INFO(u->f, "upload %s -> %s (%s)\n",
|
|
|
|
mp_imgfmt_to_name(p->last_input_fmt),
|
|
|
|
mp_imgfmt_to_name(p->last_upload_fmt),
|
|
|
|
mp_imgfmt_to_name(p->last_sw_fmt));
|
|
|
|
return true;
|
video: rewrite filtering glue code
Get rid of the old vf.c code. Replace it with a generic filtering
framework, which can potentially handle more than just --vf. At least
reimplementing --af with this code is planned.
This changes some --vf semantics (including runtime behavior and the
"vf" command). The most important ones are listed in interface-changes.
vf_convert.c is renamed to f_swscale.c. It is now an internal filter
that can not be inserted by the user manually.
f_lavfi.c is a refactor of player/lavfi.c. The latter will be removed
once --lavfi-complex is reimplemented on top of f_lavfi.c. (which is
conceptually easy, but a big mess due to the data flow changes).
The existing filters are all changed heavily. The data flow of the new
filter framework is different. Especially EOF handling changes - EOF is
now a "frame" rather than a state, and must be passed through exactly
once.
Another major thing is that all filters must support dynamic format
changes. The filter reconfig() function goes away. (This sounds complex,
but since all filters need to handle EOF draining anyway, they can use
the same code, and it removes the mess with reconfig() having to predict
the output format, which completely breaks with libavfilter anyway.)
In addition, there is no automatic format negotiation or conversion.
libavfilter's primitive and insufficient API simply doesn't allow us to
do this in a reasonable way. Instead, filters can use f_autoconvert as
sub-filter, and tell it which formats they support. This filter will in
turn add actual conversion filters, such as f_swscale, to perform
necessary format changes.
vf_vapoursynth.c uses the same basic principle of operation as before,
but with worryingly different details in data flow. Still appears to
work.
The hardware deint filters (vf_vavpp.c, vf_d3d11vpp.c, vf_vdpaupp.c) are
heavily changed. Fortunately, they all used refqueue.c, which is for
sharing the data flow logic (especially for managing future/past
surfaces and such). It turns out it can be used to factor out most of
the data flow. Some of these filters accepted software input. Instead of
having ad-hoc upload code in each filter, surface upload is now
delegated to f_autoconvert, which can use f_hwupload to perform this.
Exporting VO capabilities is still a big mess (mp_stream_info stuff).
The D3D11 code drops the redundant image formats, and all code uses the
hw_subfmt (sw_format in FFmpeg) instead. Although that too seems to be a
big mess for now.
f_async_queue is unused.
2018-01-16 10:53:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2019-10-02 19:14:58 +00:00
|
|
|
|
|
|
|
static void hwdownload_process(struct mp_filter *f)
|
|
|
|
{
|
|
|
|
struct mp_hwdownload *d = 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 (frame.type != MP_FRAME_VIDEO)
|
|
|
|
goto passthrough;
|
|
|
|
|
|
|
|
struct mp_image *src = frame.data;
|
|
|
|
if (!src->hwctx)
|
|
|
|
goto passthrough;
|
|
|
|
|
|
|
|
struct mp_image *dst = mp_image_hw_download(src, d->pool);
|
|
|
|
if (!dst) {
|
|
|
|
MP_ERR(f, "Could not copy hardware frame to CPU memory.\n");
|
|
|
|
goto passthrough;
|
|
|
|
}
|
|
|
|
|
|
|
|
mp_frame_unref(&frame);
|
|
|
|
mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_VIDEO, dst));
|
|
|
|
return;
|
|
|
|
|
|
|
|
passthrough:
|
|
|
|
mp_pin_in_write(f->ppins[1], frame);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct mp_filter_info hwdownload_filter = {
|
|
|
|
.name = "hwdownload",
|
|
|
|
.priv_size = sizeof(struct mp_hwdownload),
|
|
|
|
.process = hwdownload_process,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct mp_hwdownload *mp_hwdownload_create(struct mp_filter *parent)
|
|
|
|
{
|
|
|
|
struct mp_filter *f = mp_filter_create(parent, &hwdownload_filter);
|
|
|
|
if (!f)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
struct mp_hwdownload *d = f->priv;
|
|
|
|
|
|
|
|
d->f = f;
|
|
|
|
d->pool = mp_image_pool_new(d);
|
|
|
|
|
|
|
|
mp_filter_add_pin(f, MP_PIN_IN, "in");
|
|
|
|
mp_filter_add_pin(f, MP_PIN_OUT, "out");
|
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|