1 /*****************************************************************************
2 * chroma.c: VLC picture to VAAPI surface or vice versa
3 *****************************************************************************
4 * Copyright (C) 2017 VLC authors, VideoLAN and VideoLabs
6 * Author: Victorien Le Couviour--Tuffet <victorien.lecouviour.tuffet@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_filter.h>
33 #include <vlc_plugin.h>
35 #include "../../video_chroma/copy.h"
38 # define DEST_PICS_POOL_SZ 3
42 struct vlc_vaapi_instance
*va_inst
;
44 picture_pool_t
* dest_pics
;
45 VASurfaceID
* va_surface_ids
;
49 bool image_fallback_failed
;
52 static int CreateFallbackImage(filter_t
*filter
, picture_t
*src_pic
,
53 VADisplay va_dpy
, VAImage
*image_fallback
)
55 int count
= vaMaxNumImageFormats(va_dpy
);
57 VAImageFormat
*fmts
= vlc_alloc(count
, sizeof (*fmts
));
58 if (unlikely(fmts
== NULL
))
61 if (vaQueryImageFormats(va_dpy
, fmts
, &count
))
68 for (i
= 0; i
< count
; i
++)
69 if (fmts
[i
].fourcc
== VA_FOURCC_NV12
70 || fmts
[i
].fourcc
== VA_FOURCC_P010
)
74 if ((fmts
[i
].fourcc
== VA_FOURCC_NV12
|| fmts
[i
].fourcc
== VA_FOURCC_P010
)
75 && !vlc_vaapi_CreateImage(VLC_OBJECT(filter
), va_dpy
, &fmts
[i
],
76 src_pic
->format
.i_width
, src_pic
->format
.i_height
,
88 FillPictureFromVAImage(picture_t
*dest
,
89 VAImage
*src_img
, uint8_t *src_buf
, copy_cache_t
*cache
)
91 const uint8_t * src_planes
[2] = { src_buf
+ src_img
->offsets
[0],
92 src_buf
+ src_img
->offsets
[1] };
93 const size_t src_pitches
[2] = { src_img
->pitches
[0],
94 src_img
->pitches
[1] };
96 switch (src_img
->format
.fourcc
)
100 assert(dest
->format
.i_chroma
== VLC_CODEC_I420
);
101 Copy420_SP_to_P(dest
, src_planes
, src_pitches
, src_img
->height
, cache
);
105 switch (dest
->format
.i_chroma
)
108 Copy420_SP_to_SP(dest
, src_planes
, src_pitches
, src_img
->height
,
111 case VLC_CODEC_I420_10B
:
112 Copy420_16_SP_to_P(dest
, src_planes
, src_pitches
,
113 src_img
->height
, 0, cache
);
116 vlc_assert_unreachable();
120 vlc_assert_unreachable();
126 DownloadSurface(filter_t
*filter
, picture_t
*src_pic
)
128 filter_sys_t
*const filter_sys
= filter
->p_sys
;
129 VADisplay va_dpy
= vlc_vaapi_PicGetDisplay(src_pic
);
133 picture_t
*dest
= filter_NewPicture(filter
);
136 msg_Err(filter
, "filter_NewPicture failed");
140 VAImageID image_fallback_id
= VA_INVALID_ID
;
141 VASurfaceID surface
= vlc_vaapi_PicGetSurface(src_pic
);
142 if (vaSyncSurface(va_dpy
, surface
))
145 if (filter_sys
->derive_failed
||
146 vlc_vaapi_DeriveImage(VLC_OBJECT(filter
), va_dpy
, surface
, &src_img
))
148 if (filter_sys
->image_fallback_failed
)
151 filter_sys
->derive_failed
= true;
153 VAImage image_fallback
;
154 if (CreateFallbackImage(filter
, src_pic
, va_dpy
, &image_fallback
))
156 filter_sys
->image_fallback_failed
= true;
159 image_fallback_id
= image_fallback
.image_id
;
161 if (vaGetImage(va_dpy
, surface
, 0, 0, src_pic
->format
.i_width
,
162 src_pic
->format
.i_height
, image_fallback_id
))
164 filter_sys
->image_fallback_failed
= true;
167 src_img
= image_fallback
;
170 if (vlc_vaapi_MapBuffer(VLC_OBJECT(filter
), va_dpy
, src_img
.buf
, &src_buf
))
173 FillPictureFromVAImage(dest
, &src_img
, src_buf
, &filter
->p_sys
->cache
);
175 vlc_vaapi_UnmapBuffer(VLC_OBJECT(filter
), va_dpy
, src_img
.buf
);
176 vlc_vaapi_DestroyImage(VLC_OBJECT(filter
), va_dpy
, src_img
.image_id
);
178 picture_CopyProperties(dest
, src_pic
);
180 picture_Release(src_pic
);
184 if (image_fallback_id
!= VA_INVALID_ID
)
185 vlc_vaapi_DestroyImage(VLC_OBJECT(filter
), va_dpy
, image_fallback_id
);
187 picture_Release(dest
);
193 FillVAImageFromPicture(VAImage
*dest_img
, uint8_t *dest_buf
,
194 picture_t
*dest_pic
, picture_t
*src
,
197 const uint8_t * src_planes
[3] = { src
->p
[Y_PLANE
].p_pixels
,
198 src
->p
[U_PLANE
].p_pixels
,
199 src
->p
[V_PLANE
].p_pixels
};
200 const size_t src_pitches
[3] = { src
->p
[Y_PLANE
].i_pitch
,
201 src
->p
[U_PLANE
].i_pitch
,
202 src
->p
[V_PLANE
].i_pitch
};
203 void *const tmp
[2] = { dest_pic
->p
[0].p_pixels
,
204 dest_pic
->p
[1].p_pixels
};
206 dest_pic
->p
[0].p_pixels
= dest_buf
+ dest_img
->offsets
[0];
207 dest_pic
->p
[1].p_pixels
= dest_buf
+ dest_img
->offsets
[1];
208 dest_pic
->p
[0].i_pitch
= dest_img
->pitches
[0];
209 dest_pic
->p
[1].i_pitch
= dest_img
->pitches
[1];
211 switch (src
->format
.i_chroma
)
214 assert(dest_pic
->format
.i_chroma
== VLC_CODEC_VAAPI_420
);
215 Copy420_P_to_SP(dest_pic
, src_planes
, src_pitches
,
216 src
->format
.i_height
, cache
);
219 case VLC_CODEC_I420_10B
:
220 assert(dest_pic
->format
.i_chroma
== VLC_CODEC_VAAPI_420_10BPP
);
221 Copy420_16_P_to_SP(dest_pic
, src_planes
, src_pitches
,
222 src
->format
.i_height
, 0, cache
);
226 assert(dest_pic
->format
.i_chroma
== VLC_CODEC_VAAPI_420_10BPP
);
227 Copy420_SP_to_SP(dest_pic
, src_planes
, src_pitches
,
228 src
->format
.i_height
, cache
);
232 vlc_assert_unreachable();
235 dest_pic
->p
[0].p_pixels
= tmp
[0];
236 dest_pic
->p
[1].p_pixels
= tmp
[1];
240 UploadSurface(filter_t
*filter
, picture_t
*src
)
242 VADisplay
const va_dpy
= filter
->p_sys
->dpy
;
245 picture_t
* dest_pic
= picture_pool_Wait(filter
->p_sys
->dest_pics
);
249 msg_Err(filter
, "cannot retrieve picture from the dest pics pool");
252 vlc_vaapi_PicAttachContext(dest_pic
);
253 picture_CopyProperties(dest_pic
, src
);
255 if (vlc_vaapi_DeriveImage(VLC_OBJECT(filter
), va_dpy
,
256 vlc_vaapi_PicGetSurface(dest_pic
), &dest_img
)
257 || vlc_vaapi_MapBuffer(VLC_OBJECT(filter
), va_dpy
,
258 dest_img
.buf
, &dest_buf
))
261 FillVAImageFromPicture(&dest_img
, dest_buf
, dest_pic
,
262 src
, &filter
->p_sys
->cache
);
264 if (vlc_vaapi_UnmapBuffer(VLC_OBJECT(filter
), va_dpy
, dest_img
.buf
)
265 || vlc_vaapi_DestroyImage(VLC_OBJECT(filter
),
266 va_dpy
, dest_img
.image_id
))
270 picture_Release(src
);
274 picture_Release(dest_pic
);
279 static int CheckFmt(const video_format_t
*in
, const video_format_t
*out
,
280 bool *upload
, uint8_t *pixel_bytes
)
284 switch (in
->i_chroma
)
286 case VLC_CODEC_VAAPI_420
:
287 if (out
->i_chroma
== VLC_CODEC_I420
)
290 case VLC_CODEC_VAAPI_420_10BPP
:
291 if (out
->i_chroma
== VLC_CODEC_P010
292 || out
->i_chroma
== VLC_CODEC_I420_10B
)
301 switch (out
->i_chroma
)
303 case VLC_CODEC_VAAPI_420
:
304 if (in
->i_chroma
== VLC_CODEC_I420
)
307 case VLC_CODEC_VAAPI_420_10BPP
:
308 if (in
->i_chroma
== VLC_CODEC_P010
309 || in
->i_chroma
== VLC_CODEC_I420_10B
)
320 vlc_vaapi_OpenChroma(vlc_object_t
*obj
)
322 filter_t
*const filter
= (filter_t
*)obj
;
323 filter_sys_t
* filter_sys
;
325 if (filter
->fmt_in
.video
.i_height
!= filter
->fmt_out
.video
.i_height
326 || filter
->fmt_in
.video
.i_width
!= filter
->fmt_out
.video
.i_width
327 || filter
->fmt_in
.video
.orientation
!= filter
->fmt_out
.video
.orientation
)
332 if (CheckFmt(&filter
->fmt_in
.video
, &filter
->fmt_out
.video
, &is_upload
,
336 filter
->pf_video_filter
= is_upload
? UploadSurface
: DownloadSurface
;
338 if (!(filter_sys
= calloc(1, sizeof(filter_sys_t
))))
340 msg_Err(obj
, "unable to allocate memory");
343 filter_sys
->derive_failed
= false;
344 filter_sys
->image_fallback_failed
= false;
347 filter_sys
->va_inst
= vlc_vaapi_FilterHoldInstance(filter
,
350 if (filter_sys
->va_inst
== NULL
)
356 filter_sys
->dest_pics
=
357 vlc_vaapi_PoolNew(obj
, filter_sys
->va_inst
, filter_sys
->dpy
,
358 DEST_PICS_POOL_SZ
, &filter_sys
->va_surface_ids
,
359 &filter
->fmt_out
.video
, true);
360 if (!filter_sys
->dest_pics
)
362 vlc_vaapi_FilterReleaseInstance(filter
, filter_sys
->va_inst
);
369 /* Don't fetch the vaapi instance since it may be not created yet at
370 * this point (in case of cpu rendering) */
371 filter_sys
->va_inst
= NULL
;
372 filter_sys
->dpy
= NULL
;
373 filter_sys
->dest_pics
= NULL
;
376 if (CopyInitCache(&filter_sys
->cache
, filter
->fmt_in
.video
.i_width
381 picture_pool_Release(filter_sys
->dest_pics
);
382 vlc_vaapi_FilterReleaseInstance(filter
, filter_sys
->va_inst
);
388 filter
->p_sys
= filter_sys
;
389 msg_Warn(obj
, "Using SW chroma filter for %dx%d %4.4s -> %4.4s",
390 filter
->fmt_in
.video
.i_width
,
391 filter
->fmt_in
.video
.i_height
,
392 (const char *) &filter
->fmt_in
.video
.i_chroma
,
393 (const char *) &filter
->fmt_out
.video
.i_chroma
);
399 vlc_vaapi_CloseChroma(vlc_object_t
*obj
)
401 filter_t
*filter
= (filter_t
*)obj
;
402 filter_sys_t
*const filter_sys
= filter
->p_sys
;
404 if (filter_sys
->dest_pics
)
405 picture_pool_Release(filter_sys
->dest_pics
);
406 if (filter_sys
->va_inst
!= NULL
)
407 vlc_vaapi_FilterReleaseInstance(filter
, filter_sys
->va_inst
);
408 CopyCleanCache(&filter_sys
->cache
);