summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorxylosper <darklin20@gmail.com>2013-09-20 22:55:13 +0900
committerwm4 <wm4@nowhere>2013-09-25 13:53:42 +0200
commit39d1ab82e5a3ac30e5495c6b6773823c2ff56594 (patch)
tree03d5ab170fe12046bff32234e4e0b55d67457ce2 /video
parent1ee8d0210d8ad613e462eab10a1a4139b62de367 (diff)
downloadmpv-39d1ab82e5a3ac30e5495c6b6773823c2ff56594.tar.bz2
mpv-39d1ab82e5a3ac30e5495c6b6773823c2ff56594.tar.xz
vaapi: add vf_vavpp and use it for deinterlacing
Merged from pull request #246 by xylosper. Minor cosmetic changes, some adjustments (compatibility with older libva versions), and manpage additions by wm4. Signed-off-by: wm4 <wm4@nowhere>
Diffstat (limited to 'video')
-rw-r--r--video/decode/vaapi.c65
-rw-r--r--video/filter/vf.c4
-rw-r--r--video/filter/vf.h1
-rw-r--r--video/filter/vf_vavpp.c406
-rw-r--r--video/filter/vf_vo.c2
-rw-r--r--video/out/vo_vaapi.c546
-rw-r--r--video/vaapi.c520
-rw-r--r--video/vaapi.h77
8 files changed, 1100 insertions, 521 deletions
diff --git a/video/decode/vaapi.c b/video/decode/vaapi.c
index 9064389afe..8bec94d950 100644
--- a/video/decode/vaapi.c
+++ b/video/decode/vaapi.c
@@ -31,6 +31,7 @@
#include "video/fmt-conversion.h"
#include "video/vaapi.h"
#include "video/decode/dec_video.h"
+#include "video/filter/vf.h"
/*
* The VAAPI decoder can work only with surfaces passed to the decoder at
@@ -60,6 +61,9 @@ struct priv {
int format, w, h;
VASurfaceID surfaces[MAX_SURFACES];
+
+ struct va_surface_pool *pool;
+ int rt_format;
};
struct profile_entry {
@@ -159,36 +163,23 @@ static int is_direct_mapping(VADisplay display)
return 0;
}
-// Make vo_vaapi.c pool the required number of surfaces.
-// This is very touchy: vo_vaapi.c must not free surfaces while we decode,
-// and we must allocate only surfaces that were passed to the decoder on
-// creation.
-// We achieve this by deleting all previous surfaces, then allocate every
-// surface needed. Then we free these surfaces, and rely on the fact that
-// vo_vaapi.c keeps the released surfaces in the pool, and only allocates
-// new surfaces out of that pool.
-static int preallocate_surfaces(struct lavc_ctx *ctx, int va_rt_format, int num)
+// We must allocate only surfaces that were passed to the decoder on creation.
+// We achieve this by reserving surfaces in the pool as needed.
+// Releasing surfaces is necessary after filling the surface id list so
+// that reserved surfaces can be reused for decoding.
+static bool preallocate_surfaces(struct lavc_ctx *ctx, int num)
{
struct priv *p = ctx->hwdec_priv;
- int res = -1;
-
- struct mp_image *tmp_surfaces[MAX_SURFACES] = {0};
-
- p->ctx->flush(p->ctx); // free previously allocated surfaces
-
- for (int n = 0; n < num; n++) {
- tmp_surfaces[n] = p->ctx->get_surface(p->ctx, va_rt_format, p->format,
- p->w, p->h);
- if (!tmp_surfaces[n])
- goto done;
- p->surfaces[n] = (uintptr_t)tmp_surfaces[n]->planes[3];
+ if (!va_surface_pool_reserve(p->pool, num, p->w, p->h)) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Could not allocate surfaces.\n");
+ return false;
}
- res = 0;
-
-done:
- for (int n = 0; n < num; n++)
- talloc_free(tmp_surfaces[n]);
- return res;
+ for (int i = 0; i < num; i++) {
+ struct va_surface *s = va_surface_pool_get(p->pool, p->w, p->h);
+ p->surfaces[i] = s->id;
+ va_surface_release(s);
+ }
+ return true;
}
static void destroy_decoder(struct lavc_ctx *ctx)
@@ -274,7 +265,7 @@ static int create_decoder(struct lavc_ctx *ctx)
goto error;
}
- if (preallocate_surfaces(ctx, VA_RT_FORMAT_YUV420, num_surfaces) < 0) {
+ if (!preallocate_surfaces(ctx, num_surfaces)) {
mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Could not allocate surfaces.\n");
goto error;
}
@@ -298,7 +289,7 @@ static int create_decoder(struct lavc_ctx *ctx)
&attrib, 1);
if (!check_va_status(status, "vaGetConfigAttributes()"))
goto error;
- if ((attrib.value & VA_RT_FORMAT_YUV420) == 0) {
+ if ((attrib.value & p->rt_format) == 0) {
mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Chroma format not supported.\n");
goto error;
}
@@ -339,14 +330,13 @@ static struct mp_image *allocate_image(struct lavc_ctx *ctx, int format,
return NULL;
}
- struct mp_image *img = p->ctx->get_surface(p->ctx, VA_RT_FORMAT_YUV420,
- format, p->w, p->h);
- if (img) {
+ struct va_surface *s = va_surface_pool_get(p->pool, p->w, p->h);
+ if (s) {
for (int n = 0; n < MAX_SURFACES; n++) {
- if (p->surfaces[n] == (uintptr_t)img->planes[3])
- return img;
+ if (p->surfaces[n] == s->id)
+ return va_surface_wrap(s);
}
- talloc_free(img);
+ va_surface_release(s);
}
mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Insufficient number of surfaces.\n");
return NULL;
@@ -361,6 +351,7 @@ static void uninit(struct lavc_ctx *ctx)
destroy_decoder(ctx);
+ va_surface_pool_release(p->pool);
talloc_free(p);
ctx->hwdec_priv = NULL;
}
@@ -371,16 +362,18 @@ static int init(struct lavc_ctx *ctx)
*p = (struct priv) {
.ctx = ctx->hwdec_info->vaapi_ctx,
.va_context = &p->va_context_storage,
+ .rt_format = VA_RT_FORMAT_YUV420
};
- ctx->hwdec_priv = p;
p->display = p->ctx->display;
+ p->pool = va_surface_pool_alloc(p->display, p->rt_format);
p->va_context->display = p->display;
p->va_context->config_id = VA_INVALID_ID;
p->va_context->context_id = VA_INVALID_ID;
ctx->avctx->hwaccel_context = p->va_context;
+ ctx->hwdec_priv = p;
return 0;
}
diff --git a/video/filter/vf.c b/video/filter/vf.c
index 56ddd32fd8..216f3c42f4 100644
--- a/video/filter/vf.c
+++ b/video/filter/vf.c
@@ -70,6 +70,7 @@ extern const vf_info_t vf_info_yadif;
extern const vf_info_t vf_info_stereo3d;
extern const vf_info_t vf_info_dlopen;
extern const vf_info_t vf_info_lavfi;
+extern const vf_info_t vf_info_vaapi;
// list of available filters:
static const vf_info_t *const filter_list[] = {
@@ -112,6 +113,9 @@ static const vf_info_t *const filter_list[] = {
#ifdef CONFIG_DLOPEN
&vf_info_dlopen,
#endif
+#if CONFIG_VAAPI_VPP
+ &vf_info_vaapi,
+#endif
NULL
};
diff --git a/video/filter/vf.h b/video/filter/vf.h
index 730b0e0da0..ea1246da17 100644
--- a/video/filter/vf.h
+++ b/video/filter/vf.h
@@ -106,6 +106,7 @@ typedef struct vf_seteq {
/* Hack to make the OSD state object available to vf_sub which
* access OSD/subtitle state outside of normal OSD draw time. */
#define VFCTRL_SET_OSD_OBJ 20
+#define VFCTRL_GET_HWDEC_INFO 21 // for hwdec filters
int vf_control(struct vf_instance *vf, int cmd, void *arg);
diff --git a/video/filter/vf_vavpp.c b/video/filter/vf_vavpp.c
new file mode 100644
index 0000000000..ba4e9ca411
--- /dev/null
+++ b/video/filter/vf_vavpp.c
@@ -0,0 +1,406 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <va/va.h>
+#include <va/va_vpp.h>
+
+#include "config.h"
+#include "mpvcore/options.h"
+#include "vf.h"
+#include "video/vaapi.h"
+#include "video/decode/dec_video.h"
+
+static inline bool is_success(VAStatus status, const char *msg)
+{
+ if (status == VA_STATUS_SUCCESS)
+ return true;
+ mp_msg(MSGT_VFILTER, MSGL_ERR, "[vavpp] %s: %s\n", msg, vaErrorStr(status));
+ return false;
+}
+
+struct surface_refs {
+ VASurfaceID *surfaces;
+ int num_allocated;
+ int num_required;
+};
+
+struct pipeline {
+ VABufferID *filters;
+ int num_filters;
+ VAProcColorStandardType input_colors[VAProcColorStandardCount];
+ VAProcColorStandardType output_colors[VAProcColorStandardCount];
+ int num_input_colors, num_output_colors;
+ struct surface_refs forward, backward;
+};
+
+struct vf_priv_s {
+ double prev_pts;
+ int deint_type; // 0: none, 1: discard, 2: double fps
+ bool do_deint;
+ VABufferID buffers[VAProcFilterCount];
+ int num_buffers;
+ VAConfigID config;
+ VAContextID context;
+ struct mp_image_params params;
+ VADisplay display;
+ struct mp_vaapi_ctx *va;
+ struct pipeline pipe;
+ struct va_surface_pool *pool;
+};
+
+static const struct vf_priv_s vf_priv_default = {
+ .prev_pts = MP_NOPTS_VALUE,
+ .config = VA_INVALID_ID,
+ .context = VA_INVALID_ID,
+ .deint_type = 2,
+};
+
+static inline void realloc_refs(struct surface_refs *refs, int num)
+{
+ if (refs->num_allocated < num) {
+ refs->surfaces = realloc(refs->surfaces, sizeof(VASurfaceID)*num);
+ refs->num_allocated = num;
+ }
+ refs->num_required = num;
+}
+
+static bool update_pipeline(struct vf_priv_s *p, bool deint)
+{
+ VABufferID *filters = p->buffers;
+ int num_filters = p->num_buffers;
+ if (p->deint_type && !deint) {
+ ++filters;
+ --num_filters;
+ }
+ if (filters == p->pipe.filters && num_filters == p->pipe.num_filters)
+ return true;
+ p->pipe.forward.num_required = p->pipe.backward.num_required = 0;
+ p->pipe.num_input_colors = p->pipe.num_output_colors = 0;
+ p->pipe.num_filters = 0;
+ p->pipe.filters = NULL;
+ if (!num_filters)
+ return false;
+ VAProcPipelineCaps caps;
+ caps.input_color_standards = p->pipe.input_colors;
+ caps.output_color_standards = p->pipe.output_colors;
+ caps.num_input_color_standards = VAProcColorStandardCount;
+ caps.num_output_color_standards = VAProcColorStandardCount;
+ VAStatus status = vaQueryVideoProcPipelineCaps(p->display, p->context,
+ filters, num_filters, &caps);
+ if (!is_success(status, "vaQueryVideoProcPipelineCaps()"))
+ return false;
+ p->pipe.filters = filters;
+ p->pipe.num_filters = num_filters;
+ p->pipe.num_input_colors = caps.num_input_color_standards;
+ p->pipe.num_output_colors = caps.num_output_color_standards;
+ realloc_refs(&p->pipe.forward, caps.num_forward_references);
+ realloc_refs(&p->pipe.backward, caps.num_backward_references);
+ return true;
+}
+
+static inline int get_deint_field(struct vf_priv_s *p, int i,
+ const struct mp_image *mpi)
+{
+ if (!p->do_deint || !(mpi->fields & MP_IMGFIELD_INTERLACED))
+ return VA_FRAME_PICTURE;
+ return !!(mpi->fields & MP_IMGFIELD_TOP_FIRST) ^ i ? VA_TOP_FIELD : VA_BOTTOM_FIELD;
+}
+
+static struct mp_image *render(struct vf_priv_s *p, struct va_surface *in,
+ unsigned int flags)
+{
+ if (!p->pipe.filters || !in)
+ return NULL;
+ struct va_surface *out = va_surface_pool_get(p->pool, in->w, in->h);
+ if (!out)
+ return NULL;
+ enum {Begun = 1, Rendered = 2};
+ int state = 0;
+ do { // not a loop, just for break
+ VAStatus status = vaBeginPicture(p->display, p->context, out->id);
+ if (!is_success(status, "vaBeginPicture()"))
+ break;
+ state |= Begun;
+ VABufferID buffer = VA_INVALID_ID;
+ VAProcPipelineParameterBuffer *param = NULL;
+ status = vaCreateBuffer(p->display, p->context,
+ VAProcPipelineParameterBufferType,
+ sizeof(*param), 1, NULL, &buffer);
+ if (!is_success(status, "vaCreateBuffer()"))
+ break;
+ status = vaMapBuffer(p->display, buffer, (void**)&param);
+ if (!is_success(status, "vaMapBuffer()"))
+ break;
+ param->surface = in->id;
+ param->surface_region = NULL;
+ param->output_region = NULL;
+ param->output_background_color = 0;
+ param->filter_flags = flags;
+ param->filters = p->pipe.filters;
+ param->num_filters = p->pipe.num_filters;
+ vaUnmapBuffer(p->display, buffer);
+ param->forward_references = p->pipe.forward.surfaces;
+ param->backward_references = p->pipe.backward.surfaces;
+ param->num_forward_references = p->pipe.forward.num_required;
+ param->num_backward_references = p->pipe.backward.num_required;
+ status = vaRenderPicture(p->display, p->context, &buffer, 1);
+ if (!is_success(status, "vaRenderPicture()"))
+ break;
+ state |= Rendered;
+ } while (false);
+ if (state & Begun)
+ vaEndPicture(p->display, p->context);
+ if (state & Rendered)
+ return va_surface_wrap(out);
+ va_surface_release(out);
+ return NULL;
+}
+
+// return value: the number of created images
+static int process(struct vf_priv_s *p, struct mp_image *in,
+ struct mp_image **out1, struct mp_image **out2)
+{
+ const bool deint = p->do_deint && p->deint_type > 0;
+ if (!update_pipeline(p, deint) || !p->pipe.filters) // no filtering
+ return 0;
+ struct va_surface *surface = va_surface_in_mp_image(in);
+ const unsigned int csp = va_get_colorspace_flag(p->params.colorspace);
+ const unsigned int field = get_deint_field(p, 0, in);
+ *out1 = render(p, surface, field | csp);
+ if (!*out1) // cannot render
+ return 0;
+ mp_image_copy_attributes(*out1, in);
+ if (field == VA_FRAME_PICTURE || p->deint_type < 2) // first-field only
+ return 1;
+ const double add = (in->pts - p->prev_pts)*0.5;
+ if (p->prev_pts == MP_NOPTS_VALUE || add <= 0.0 || add > 0.5) // no pts, skip it
+ return 1;
+ *out2 = render(p, surface, get_deint_field(p, 1, in) | csp);
+ if (!*out2) // cannot render
+ return 1;
+ mp_image_copy_attributes(*out2, in);
+ (*out2)->pts = in->pts + add;
+ return 2;
+}
+
+static struct mp_image *upload(struct vf_priv_s *p, struct mp_image *in)
+{
+ struct va_surface *surface =
+ va_surface_pool_get_by_imgfmt(p->pool, p->va->image_formats, in->imgfmt, in->w, in->h);
+ if (!surface)
+ surface = va_surface_pool_get(p->pool, in->w, in->h); // dummy
+ else
+ va_surface_upload(surface, in);
+ struct mp_image *out = va_surface_wrap(surface);
+ mp_image_copy_attributes(out, in);
+ return out;
+}
+
+static int filter_ext(struct vf_instance *vf, struct mp_image *in)
+{
+ struct vf_priv_s *p = vf->priv;
+ struct va_surface *surface = va_surface_in_mp_image(in);
+ const int rt_format = surface ? surface->rt_format : VA_RT_FORMAT_YUV420;
+ if (!p->pool || va_surface_pool_rt_format(p->pool) != rt_format) {
+ va_surface_pool_release(p->pool);
+ p->pool = va_surface_pool_alloc(p->display, rt_format);
+ }
+ if (!surface) {
+ struct mp_image *tmp = upload(p, in);
+ talloc_free(in);
+ in = tmp;
+ }
+
+ struct mp_image *out1, *out2;
+ const double pts = in->pts;
+ const int num = process(p, in, &out1, &out2);
+ if (!num)
+ vf_add_output_frame(vf, in);
+ else {
+ vf_add_output_frame(vf, out1);
+ if (num > 1)
+ vf_add_output_frame(vf, out2);
+ talloc_free(in);
+ }
+ p->prev_pts = pts;
+ return 0;
+}
+
+static int reconfig(struct vf_instance *vf, struct mp_image_params *params,
+ int flags)
+{
+ struct vf_priv_s *p = vf->priv;
+
+ p->prev_pts = MP_NOPTS_VALUE;
+ p->params = *params;
+ params->imgfmt = IMGFMT_VAAPI;
+ return vf_next_reconfig(vf, params, flags);
+}
+
+static void uninit(struct vf_instance *vf)
+{
+ struct vf_priv_s *p = vf->priv;
+ for (int i=0; i<p->num_buffers; ++i)
+ vaDestroyBuffer(p->display, p->buffers[i]);
+ if (p->context != VA_INVALID_ID)
+ vaDestroyContext(p->display, p->context);
+ if (p->config != VA_INVALID_ID)
+ vaDestroyConfig(p->display, p->config);
+ free(p->pipe.forward.surfaces);
+ free(p->pipe.backward.surfaces);
+ va_surface_pool_release(p->pool);
+}
+
+static int query_format(struct vf_instance *vf, unsigned int imgfmt)
+{
+ struct vf_priv_s *p = vf->priv;
+ if (IMGFMT_IS_VAAPI(imgfmt) || va_image_format_from_imgfmt(p->va->image_formats, imgfmt))
+ return vf_next_query_format(vf, IMGFMT_VAAPI);
+ return 0;
+}
+
+static int control(struct vf_instance *vf, int request, void* data)
+{
+ struct vf_priv_s *p = vf->priv;
+ switch (request){
+ case VFCTRL_GET_DEINTERLACE:
+ *(int*)data = !!p->do_deint;
+ return true;
+ case VFCTRL_SET_DEINTERLACE:
+ p->do_deint = *(int*)data;
+ return true;
+ default:
+ return vf_next_control (vf, request, data);
+ }
+}
+
+static int va_query_filter_caps(struct vf_priv_s *p, VAProcFilterType type,
+ void *caps, unsigned int count)
+{
+ VAStatus status = vaQueryVideoProcFilterCaps(p->display, p->context, type,
+ caps, &count);
+ return is_success(status, "vaQueryVideoProcFilterCaps()") ? count : 0;
+}
+
+static VABufferID va_create_filter_buffer(struct vf_priv_s *p, int bytes,
+ int num, void *data)
+{
+ VABufferID buffer;
+ VAStatus status = vaCreateBuffer(p->display, p->context,
+ VAProcFilterParameterBufferType,
+ bytes, num, data, &buffer);
+ return is_success(status, "vaCreateBuffer()") ? buffer : VA_INVALID_ID;
+}
+
+static bool initialize(struct vf_priv_s *p)
+{
+ VAStatus status;
+
+ VAConfigID config;
+ status = vaCreateConfig(p->display, VAProfileNone, VAEntrypointVideoProc,
+ NULL, 0, &config);
+ if (!is_success(status, "vaCreateConfig()")) // no entrypoint for video porc
+ return false;
+ p->config = config;
+
+ VAContextID context;
+ status = vaCreateContext(p->display, p->config, 0, 0, 0, NULL, 0, &context);
+ if (!is_success(status, "vaCreateContext()"))
+ return false;
+ p->context = context;
+
+ VAProcFilterType filters[VAProcFilterCount];
+ int num_filters = VAProcFilterCount;
+ status = vaQueryVideoProcFilters(p->display, p->context, filters, &num_filters);
+ if (!is_success(status, "vaQueryVideoProcFilters()"))
+ return false;
+
+ VABufferID buffers[VAProcFilterCount];
+ for (int i=0; i<VAProcFilterCount; ++i)
+ buffers[i] = VA_INVALID_ID;
+ for (int i=0; i<num_filters; ++i) {
+ if (filters[i] == VAProcFilterDeinterlacing) {
+ if (!p->deint_type)
+ continue;
+ VAProcFilterCapDeinterlacing caps[VAProcDeinterlacingCount];
+ int num = va_query_filter_caps(p, VAProcFilterDeinterlacing, caps,
+ VAProcDeinterlacingCount);
+ if (!num)
+ continue;
+ VAProcDeinterlacingType algorithm = VAProcDeinterlacingBob;
+ for (int i=0; i<num; ++i) { // find Bob
+ if (caps[i].type != algorithm)
+ continue;
+ VAProcFilterParameterBufferDeinterlacing param;
+ param.type = VAProcFilterDeinterlacing;
+ param.algorithm = algorithm;
+ buffers[VAProcFilterDeinterlacing] =
+ va_create_filter_buffer(p, sizeof(param), 1, &param);
+ }
+ } // check other filters
+ }
+ p->num_buffers = 0;
+ if (buffers[VAProcFilterDeinterlacing] != VA_INVALID_ID)
+ p->buffers[p->num_buffers++] = buffers[VAProcFilterDeinterlacing];
+ else
+ p->deint_type = 0;
+ p->do_deint = !!p->deint_type;
+ // next filters: p->buffers[p->num_buffers++] = buffers[next_filter];
+ return true;
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ vf->reconfig = reconfig;
+ vf->filter_ext = filter_ext;
+ vf->query_format = query_format;
+ vf->uninit = uninit;
+ vf->control = control;
+
+ struct vf_priv_s *p = vf->priv;
+ struct mp_hwdec_info hwdec;
+ if (vf_control(vf->next, VFCTRL_GET_HWDEC_INFO, &hwdec) <= 0)
+ return false;
+ p->va = hwdec.vaapi_ctx;
+ if (!p->va || !p->va->display)
+ return false;
+ p->display = p->va->display;
+ if (initialize(p))
+ return true;
+ uninit(vf);
+ return false;
+}
+
+#define OPT_BASE_STRUCT struct vf_priv_s
+static const m_option_t vf_opts_fields[] = {
+ OPT_CHOICE("deint", deint_type, 0,
+ ({"no", 0},
+ {"first-field", 1},
+ {"bob", 2})),
+ {0}
+};
+
+const vf_info_t vf_info_vaapi = {
+ .info = "VA-API Video Post-Process Filter",
+ .name = "vavpp",
+ .author = "xylosper",
+ .comment = "",
+ .vf_open = vf_open,
+ .priv_size = sizeof(struct vf_priv_s),
+ .priv_defaults = &vf_priv_default,
+ .options = vf_opts_fields,
+};
diff --git a/video/filter/vf_vo.c b/video/filter/vf_vo.c
index 05f835d5fd..cccfb45fc2 100644
--- a/video/filter/vf_vo.c
+++ b/video/filter/vf_vo.c
@@ -83,6 +83,8 @@ static int control(struct vf_instance *vf, int request, void *data)
};
return vo_control(video_out, VOCTRL_GET_EQUALIZER, &param) == VO_TRUE;
}
+ case VFCTRL_GET_HWDEC_INFO:
+ return vo_control(video_out, VOCTRL_GET_HWDEC_INFO, data) == VO_TRUE;
}
return CONTROL_UNKNOWN;
}
diff --git a/video/out/vo_vaapi.c b/video/out/vo_vaapi.c
index d7947cda20..dd7f138a24 100644
--- a/video/out/vo_vaapi.c
+++ b/video/out/vo_vaapi.c
@@ -43,22 +43,6 @@
#include "video/vaapi.h"
#include "video/decode/dec_video.h"
-#define STR_FOURCC(fcc) \
- (const char[]){(fcc), (fcc) >> 8u, (fcc) >> 16u, (fcc) >> 24u, 0}
-
-struct vaapi_surface {
- VASurfaceID id; // VA_INVALID_ID if unallocated
- int w, h, va_format; // parameters of allocated image (0/0/-1 unallocated)
- VAImage image; // used for sofwtare decoding case
- bool is_bound; // image bound to the surface?
- bool is_used; // referenced by a mp_image
- bool is_dead; // used, but deallocate VA objects as soon as possible
- int order; // for LRU allocation
-
- // convenience shortcut for mp_image deallocation callback
- struct priv *p;
-};
-
struct vaapi_osd_image {
int w, h;
VAImage image;
@@ -86,7 +70,7 @@ struct priv {
struct mp_log *log;
struct vo *vo;
VADisplay display;
- struct mp_vaapi_ctx mpvaapi;
+ struct mp_vaapi_ctx *mpvaapi;
struct mp_image_params image_params;
struct mp_rect src_rect;
@@ -98,21 +82,18 @@ struct priv {
int output_surface;
int visible_surface;
- int deint;
- int deint_type;
int scaling;
int force_scaled_osd;
+ // with old libva versions only
+ int deint;
+ int deint_type;
VAImageFormat osd_format; // corresponds to OSD_VA_FORMAT
struct vaapi_osd_part osd_parts[MAX_OSD_PARTS];
bool osd_screen;
- int num_video_surfaces;
- struct vaapi_surface **video_surfaces;
- int video_surface_lru_counter;
-
- VAImageFormat *va_image_formats;
- int va_num_image_formats;
+ struct va_surface_pool *pool;
+ struct va_image_formats *va_image_formats;
VAImageFormat *va_subpic_formats;
unsigned int *va_subpic_flags;
int va_num_subpic_formats;
@@ -128,187 +109,10 @@ static const bool osd_formats[SUBBITMAP_COUNT] = {
[SUBBITMAP_RGBA] = true,
};
-struct fmtentry {
- uint32_t va;
- int mp;
-};
-static struct fmtentry va_to_imgfmt[] = {
- {VA_FOURCC('Y','V','1','2'), IMGFMT_420P},
- {VA_FOURCC('I','4','2','0'), IMGFMT_420P},
- {VA_FOURCC('I','Y','U','V'), IMGFMT_420P},
- {VA_FOURCC('N','V','1','2'), IMGFMT_NV12},
- // Note: not sure about endian issues (the mp formats are byte-addressed)
- {VA_FOURCC_RGBA, IMGFMT_RGBA},
- {VA_FOURCC_BGRA, IMGFMT_BGRA},
- // Untested.
- //{VA_FOURCC_UYVY, IMGFMT_UYVY},
- //{VA_FOURCC_YUY2, IMGFMT_YUYV},
- {0}
-};
-
-
-static int va_fourcc_to_imgfmt(uint32_t fourcc)
-{
- for (int n = 0; va_to_imgfmt[n].mp; n++) {
- if (va_to_imgfmt[n].va == fourcc)
- return va_to_imgfmt[n].mp;
- }
- return 0;
-}
-
-static VAImageFormat *VAImageFormat_from_imgfmt(struct priv *p, int format)
-{
- for (int i = 0; i < p->va_num_image_formats; i++) {
- if (va_fourcc_to_imgfmt(p->va_image_formats[i].fourcc) == format)
- return &p->va_image_formats[i];
- }
- return NULL;
-}
-
-static struct vaapi_surface *to_vaapi_surface(struct priv *p,
- struct mp_image *img)
-{
- if (!img || !IMGFMT_IS_VAAPI(img->imgfmt))
- return NULL;
- // Note: we _could_ use planes[1] or planes[2] to store a vaapi_surface
- // pointer, but I just don't trust libavcodec enough.
- VASurfaceID id = (uintptr_t)img->planes[3];
- for (int n = 0; n < p->num_video_surfaces; n++) {
- struct vaapi_surface *s = p->video_surfaces[n];
- if (s->id == id)
- return s;
- }
- return NULL;
-}
-
-static struct vaapi_surface *alloc_vaapi_surface(struct priv *p, int w, int h,
- int va_format)
-{
- VAStatus status;
-
- VASurfaceID id = VA_INVALID_ID;
- status = vaCreateSurfaces(p->display, w, h, va_format, 1, &id);
- if (!check_va_status(status, "vaCreateSurfaces()"))
- return NULL;
-
- struct vaapi_surface *surface = NULL;
- for (int n = 0; n < p->num_video_surfaces; n++) {
- struct vaapi_surface *s = p->video_surfaces[n];
- if (s->id == VA_INVALID_ID) {
- surface = s;
- break;
- }
- }
- if (!surface) {
- surface = talloc_ptrtype(NULL, surface);
- MP_TARRAY_APPEND(p, p->video_surfaces, p->num_video_surfaces, surface);
- }
-
- *surface = (struct vaapi_surface) {
- .id = id,
- .image = { .image_id = VA_INVALID_ID, .buf = VA_INVALID_ID },
- .w = w,
- .h = h,
- .va_format = va_format,
- .p = p,
- };
- return surface;
-}
-
-static void destroy_vaapi_surface(struct priv *p, struct vaapi_surface *s)
-{
- if (!s || s->id == VA_INVALID_ID)
- return;
- assert(!s->is_used);
-
- if (s->image.image_id != VA_INVALID_ID)
- vaDestroyImage(p->display, s->image.image_id);
- vaDestroySurfaces(p->display, &s->id, 1);
- s->id = VA_INVALID_ID;
- s->w = 0;
- s->h = 0;
- s->va_format = -1;
-}
-
-static struct vaapi_surface *get_vaapi_surface(struct priv *p, int w, int h,
- int va_format)
-{
- struct vaapi_surface *best = NULL;
-
- for (int n = 0; n < p->num_video_surfaces; n++) {
- struct vaapi_surface *s = p->video_surfaces[n];
- if (!s->is_used && s->w == w && s->h == h && s->va_format == va_format) {
- if (!best || best->order > s->order)
- best = s;
- }
- }
-
- if (!best)
- best = alloc_vaapi_surface(p, w, h, va_format);
-
- if (best) {
- best->is_used = true;
- best->order = ++p->video_surface_lru_counter;
- }
- return best;
-}
-
-static void release_video_surface(void *ptr)
-{
- struct vaapi_surface *surface = ptr;
- surface->is_used = false;
- if (surface->is_dead)
- destroy_vaapi_surface(surface->p, surface);
-}
-
-static struct mp_image *get_surface(struct mp_vaapi_ctx *ctx, int va_rt_format,
- int mp_format, int w, int h)
-{
- assert(IMGFMT_IS_VAAPI(mp_format));
-
- struct vo *vo = ctx->priv;
- struct priv *p = vo->priv;
-
- struct mp_image img = {0};
- mp_image_setfmt(&img, mp_format);
- mp_image_set_size(&img, w, h);
-
- struct vaapi_surface *surface = get_vaapi_surface(p, w, h, va_rt_format);
- if (!surface)
- return NULL;
-
- // libavcodec probably wants it at [0] and [3]
- // [1] and [2] are possibly free for own use.
- for (int n = 0; n < 4; n++)
- img.planes[n] = (void *)(uintptr_t)surface->id;
-
- return mp_image_new_custom_ref(&img, surface, release_video_surface);
-}
-
-// This should be called only by code that is going to preallocate surfaces
-// (and by uninit). Otherwise, hw decoder init might get confused by
-// accidentally releasing hw decoder preallocated surfaces.
-static void flush_surfaces(struct mp_vaapi_ctx *ctx)
-{
- struct vo *vo = ctx->priv;
- struct priv *p = vo->priv;
-
- for (int n = 0; n < p->num_video_surfaces; n++) {
- struct vaapi_surface *s = p->video_surfaces[n];
- if (s->is_used) {
- s->is_dead = true;
- } else {
- destroy_vaapi_surface(p, s);
- }
- }
-}
-
static void flush_output_surfaces(struct priv *p)
{
- for (int n = 0; n < MAX_OUTPUT_SURFACES; n++) {
- talloc_free(p->output_surfaces[n]);
- p->output_surfaces[n] = NULL;
- }
+ for (int n = 0; n < MAX_OUTPUT_SURFACES; n++)
+ mp_image_unrefp(&p->output_surfaces[n]);
p->output_surface = 0;
p->visible_surface = 0;
}
@@ -318,60 +122,20 @@ static void free_video_specific(struct priv *p)
{
flush_output_surfaces(p);
- for (int n = 0; n < MAX_OUTPUT_SURFACES; n++) {
- talloc_free(p->swdec_surfaces[n]);
- p->swdec_surfaces[n] = NULL;
- }
-
- flush_surfaces(&p->mpvaapi);
+ for (int n = 0; n < MAX_OUTPUT_SURFACES; n++)
+ mp_image_unrefp(&p->swdec_surfaces[n]);
}
-static int alloc_swdec_surfaces(struct priv *p, int w, int h, int format)
+static bool alloc_swdec_surfaces(struct priv *p, int w, int h, int imgfmt)
{
- VAStatus status;
-
free_video_specific(p);
-
- VAImageFormat *image_format = VAImageFormat_from_imgfmt(p, format);
- if (!image_format)
- return -1;
for (int i = 0; i < MAX_OUTPUT_SURFACES; i++) {
- // WTF: no mapping from VAImageFormat -> VA_RT_FORMAT_
- struct mp_image *img =
- get_surface(&p->mpvaapi, VA_RT_FORMAT_YUV420, IMGFMT_VAAPI, w, h);
- struct vaapi_surface *s = to_vaapi_surface(p, img);
- if (!s)
- return -1;
-
- if (s->image.image_id != VA_INVALID_ID) {
- vaDestroyImage(p->display, s->image.image_id);
- s->image.image_id = VA_INVALID_ID;
- }
-
- status = vaDeriveImage(p->display, s->id, &s->image);
- if (status == VA_STATUS_SUCCESS) {
- /* vaDeriveImage() is supported, check format */
- if (s->image.format.fourcc == image_format->fourcc &&
- s->image.width == w && s->image.height == h)
- {
- s->is_bound = true;
- MP_VERBOSE(p, "Using vaDeriveImage()\n");
- } else {
- vaDestroyImage(p->display, s->image.image_id);
- s->image.image_id = VA_INVALID_ID;
- status = VA_STATUS_ERROR_OPERATION_FAILED;
- }
- }
- if (status != VA_STATUS_SUCCESS) {
- status = vaCreateImage(p->display, image_format, w, h, &s->image);
- if (!check_va_status(status, "vaCreateImage()")) {
- talloc_free(img);
- return -1;
- }
- }
- p->swdec_surfaces[i] = img;
+ p->swdec_surfaces[i] =
+ va_surface_pool_get_wrapped(p->pool, p->va_image_formats, imgfmt, w, h);
+ if (!p->swdec_surfaces[i])
+ return false;
}
- return 0;
+ return true;
}
static void resize(struct priv *p)
@@ -392,7 +156,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
flags, "vaapi");
if (!IMGFMT_IS_VAAPI(params->imgfmt)) {
- if (alloc_swdec_surfaces(p, params->w, params->h, params->imgfmt) < 0)
+ if (!alloc_swdec_surfaces(p, params->w, params->h, params->imgfmt))
return -1;
}
@@ -401,42 +165,22 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
return 0;
}
-static int query_format(stru