chroma: copy: add a way to shift bits for 16 bits conversion
[vlc.git] / modules / hw / vaapi / chroma.c
blob5fc965283a6459a962a7112dda783c681c339167
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 *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <assert.h>
29 #include <va/va.h>
31 #include <vlc_common.h>
32 #include <vlc_filter.h>
33 #include <vlc_plugin.h>
35 #include "../../video_chroma/copy.h"
36 #include "filters.h"
38 # define DEST_PICS_POOL_SZ 3
40 struct filter_sys_t
42 struct vlc_vaapi_instance *va_inst;
43 VADisplay dpy;
44 picture_pool_t * dest_pics;
45 VASurfaceID * va_surface_ids;
46 copy_cache_t cache;
48 bool derive_failed;
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))
59 return VLC_ENOMEM;
61 if (vaQueryImageFormats(va_dpy, fmts, &count))
63 free(fmts);
64 return VLC_EGENERIC;
67 int i;
68 for (i = 0; i < count; i++)
69 if (fmts[i].fourcc == VA_FOURCC_NV12
70 || fmts[i].fourcc == VA_FOURCC_P010)
71 break;
73 int ret;
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,
77 image_fallback))
78 ret = VLC_SUCCESS;
79 else
80 ret = VLC_EGENERIC;
82 free(fmts);
84 return ret;
87 static inline void
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)
98 case VA_FOURCC_NV12:
100 assert(dest->format.i_chroma == VLC_CODEC_I420);
101 Copy420_SP_to_P(dest, src_planes, src_pitches, src_img->height, cache);
102 break;
104 case VA_FOURCC_P010:
105 switch (dest->format.i_chroma)
107 case VLC_CODEC_P010:
108 Copy420_SP_to_SP(dest, src_planes, src_pitches, src_img->height,
109 cache);
110 break;
111 case VLC_CODEC_I420_10B:
112 Copy420_16_SP_to_P(dest, src_planes, src_pitches,
113 src_img->height, 0, cache);
114 break;
115 default:
116 vlc_assert_unreachable();
118 break;
119 default:
120 vlc_assert_unreachable();
121 break;
125 static picture_t *
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);
130 VAImage src_img;
131 void * src_buf;
133 picture_t *dest = filter_NewPicture(filter);
134 if (!dest)
136 msg_Err(filter, "filter_NewPicture failed");
137 goto ret;
140 VAImageID image_fallback_id = VA_INVALID_ID;
141 VASurfaceID surface = vlc_vaapi_PicGetSurface(src_pic);
142 if (vaSyncSurface(va_dpy, surface))
143 goto error;
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)
149 goto error;
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;
157 goto error;
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;
165 goto error;
167 src_img = image_fallback;
170 if (vlc_vaapi_MapBuffer(VLC_OBJECT(filter), va_dpy, src_img.buf, &src_buf))
171 goto error;
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);
179 ret:
180 picture_Release(src_pic);
181 return dest;
183 error:
184 if (image_fallback_id != VA_INVALID_ID)
185 vlc_vaapi_DestroyImage(VLC_OBJECT(filter), va_dpy, image_fallback_id);
187 picture_Release(dest);
188 dest = NULL;
189 goto ret;
192 static inline void
193 FillVAImageFromPicture(VAImage *dest_img, uint8_t *dest_buf,
194 picture_t *dest_pic, picture_t *src,
195 copy_cache_t *cache)
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)
213 case VLC_CODEC_I420:
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);
218 break;
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);
223 break;
224 case VLC_CODEC_P010:
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);
229 break;
231 default:
232 vlc_assert_unreachable();
235 dest_pic->p[0].p_pixels = tmp[0];
236 dest_pic->p[1].p_pixels = tmp[1];
239 static picture_t *
240 UploadSurface(filter_t *filter, picture_t *src)
242 VADisplay const va_dpy = filter->p_sys->dpy;
243 VAImage dest_img;
244 void * dest_buf;
245 picture_t * dest_pic = picture_pool_Wait(filter->p_sys->dest_pics);
247 if (!dest_pic)
249 msg_Err(filter, "cannot retrieve picture from the dest pics pool");
250 goto ret;
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))
259 goto error;
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))
267 goto error;
269 ret:
270 picture_Release(src);
271 return dest_pic;
273 error:
274 picture_Release(dest_pic);
275 dest_pic = NULL;
276 goto ret;
279 static int CheckFmt(const video_format_t *in, const video_format_t *out,
280 bool *upload, uint8_t *pixel_bytes)
282 *pixel_bytes = 1;
283 *upload = false;
284 switch (in->i_chroma)
286 case VLC_CODEC_VAAPI_420:
287 if (out->i_chroma == VLC_CODEC_I420)
288 return VLC_SUCCESS;
289 break;
290 case VLC_CODEC_VAAPI_420_10BPP:
291 if (out->i_chroma == VLC_CODEC_P010
292 || out->i_chroma == VLC_CODEC_I420_10B)
294 *pixel_bytes = 2;
295 return VLC_SUCCESS;
297 break;
300 *upload = true;
301 switch (out->i_chroma)
303 case VLC_CODEC_VAAPI_420:
304 if (in->i_chroma == VLC_CODEC_I420)
305 return VLC_SUCCESS;
306 break;
307 case VLC_CODEC_VAAPI_420_10BPP:
308 if (in->i_chroma == VLC_CODEC_P010
309 || in->i_chroma == VLC_CODEC_I420_10B)
311 *pixel_bytes = 2;
312 return VLC_SUCCESS;
314 break;
316 return VLC_EGENERIC;
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)
328 return VLC_EGENERIC;
330 bool is_upload;
331 uint8_t pixel_bytes;
332 if (CheckFmt(&filter->fmt_in.video, &filter->fmt_out.video, &is_upload,
333 &pixel_bytes))
334 return VLC_EGENERIC;
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");
341 return VLC_ENOMEM;
343 filter_sys->derive_failed = false;
344 filter_sys->image_fallback_failed = false;
345 if (is_upload)
347 filter_sys->va_inst = vlc_vaapi_FilterHoldInstance(filter,
348 &filter_sys->dpy);
350 if (filter_sys->va_inst == NULL)
352 free(filter_sys);
353 return VLC_EGENERIC;
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);
363 free(filter_sys);
364 return VLC_EGENERIC;
367 else
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
377 * pixel_bytes))
379 if (is_upload)
381 picture_pool_Release(filter_sys->dest_pics);
382 vlc_vaapi_FilterReleaseInstance(filter, filter_sys->va_inst);
384 free(filter_sys);
385 return VLC_EGENERIC;
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);
395 return VLC_SUCCESS;
398 void
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);
410 free(filter_sys);