1 /*****************************************************************************
2 * vaapi.c: VAAPI helpers for the ffmpeg decoder
3 *****************************************************************************
4 * Copyright (C) 2009 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir_AT_ videolan _DOT_ org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
28 #include <vlc_common.h>
32 #ifdef HAVE_LIBAVCODEC_AVCODEC_H
33 # include <libavcodec/avcodec.h>
34 #elif defined(HAVE_FFMPEG_AVCODEC_H)
35 # include <ffmpeg/avcodec.h>
43 #ifdef HAVE_AVCODEC_VAAPI
45 #include <libavcodec/vaapi.h>
48 #include <va/va_x11.h>
63 Display *p_display_x11;
66 VAConfigID i_config_id;
67 VAContextID i_context_id;
69 struct vaapi_context hw_ctx;
77 unsigned int i_surface_order;
80 vlc_fourcc_t i_surface_chroma;
82 vlc_va_surface_t *p_surface;
88 static vlc_va_vaapi_t *vlc_va_vaapi_Get( void *p_va )
94 static int Open( vlc_va_vaapi_t *p_va, int i_codec_id )
102 case CODEC_ID_MPEG1VIDEO:
103 case CODEC_ID_MPEG2VIDEO:
104 i_profile = VAProfileMPEG2Main;
105 i_surface_count = 2+1;
108 i_profile = VAProfileMPEG4AdvancedSimple;
109 i_surface_count = 2+1;
112 i_profile = VAProfileVC1Main;
113 i_surface_count = 2+1;
116 i_profile = VAProfileVC1Advanced;
117 i_surface_count = 2+1;
120 i_profile = VAProfileH264High;
121 i_surface_count = 16+1;
128 memset( p_va, 0, sizeof(*p_va) );
130 /* Create a VA display */
131 p_va->p_display_x11 = XOpenDisplay(NULL);
132 if( !p_va->p_display_x11 )
135 p_va->p_display = vaGetDisplay( p_va->p_display_x11 );
136 if( !p_va->p_display )
139 if( vaInitialize( p_va->p_display, &p_va->i_version_major, &p_va->i_version_minor ) )
142 /* Create a VA configuration */
143 VAConfigAttrib attrib;
144 memset( &attrib, 0, sizeof(attrib) );
145 attrib.type = VAConfigAttribRTFormat;
146 if( vaGetConfigAttributes( p_va->p_display,
147 i_profile, VAEntrypointVLD, &attrib, 1 ) )
150 /* Not sure what to do if not, I don't have a way to test */
151 if( (attrib.value & VA_RT_FORMAT_YUV420) == 0 )
153 if( vaCreateConfig( p_va->p_display,
154 i_profile, VAEntrypointVLD, &attrib, 1, &p_va->i_config_id ) )
156 p_va->i_config_id = 0;
160 p_va->i_surface_count = i_surface_count;
162 if( asprintf( &p_va->va.description, "VA API version %d.%d",
163 p_va->i_version_major, p_va->i_version_minor ) < 0 )
164 p_va->va.description = NULL;
172 static void DestroySurfaces( vlc_va_vaapi_t *p_va )
174 if( p_va->image.image_id )
175 vaDestroyImage( p_va->p_display, p_va->image.image_id );
177 if( p_va->i_context_id )
178 vaDestroyContext( p_va->p_display, p_va->i_context_id );
180 for( int i = 0; i < p_va->i_surface_count && p_va->p_surface; i++ )
182 vlc_va_surface_t *p_surface = &p_va->p_surface[i];
184 if( p_surface->i_id != VA_INVALID_SURFACE )
185 vaDestroySurfaces( p_va->p_display, &p_surface->i_id, 1 );
187 free( p_va->p_surface );
190 p_va->image.image_id = 0;
191 p_va->i_context_id = 0;
192 p_va->p_surface = NULL;
193 p_va->i_surface_width = 0;
194 p_va->i_surface_height = 0;
196 static int CreateSurfaces( vlc_va_vaapi_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
197 int i_width, int i_height )
199 assert( i_width > 0 && i_height > 0 );
202 p_va->p_surface = calloc( p_va->i_surface_count, sizeof(*p_va->p_surface) );
203 if( !p_va->p_surface )
206 /* Create surfaces */
207 VASurfaceID pi_surface_id[p_va->i_surface_count];
208 if( vaCreateSurfaces( p_va->p_display, i_width, i_height, VA_RT_FORMAT_YUV420,
209 p_va->i_surface_count, pi_surface_id ) )
211 for( int i = 0; i < p_va->i_surface_count; i++ )
212 p_va->p_surface[i].i_id = VA_INVALID_SURFACE;
216 for( int i = 0; i < p_va->i_surface_count; i++ )
218 vlc_va_surface_t *p_surface = &p_va->p_surface[i];
220 p_surface->i_id = pi_surface_id[i];
221 p_surface->i_refcount = 0;
222 p_surface->i_order = 0;
225 /* Create a context */
226 if( vaCreateContext( p_va->p_display, p_va->i_config_id,
227 i_width, i_height, VA_PROGRESSIVE,
228 pi_surface_id, p_va->i_surface_count, &p_va->i_context_id ) )
230 p_va->i_context_id = 0;
234 /* Find a supported image chroma */
235 int i_fmt_count = vaMaxNumImageFormats( p_va->p_display );
236 VAImageFormat *p_fmt = calloc( i_fmt_count, sizeof(*p_fmt) );
240 if( vaQueryImageFormats( p_va->p_display, p_fmt, &i_fmt_count ) )
246 vlc_fourcc_t i_chroma = 0;
248 for( int i = 0; i < i_fmt_count; i++ )
250 if( p_fmt[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) ||
251 p_fmt[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) )
253 i_chroma = VLC_FOURCC( 'I', '4', '2', '0' );
256 /* TODO: It seems that these may also be available (but not
258 * VA_FOURCC( 'N', 'V', '1', '2')
259 * VA_FOURCC( 'U', 'Y', 'V', 'Y')
260 * VA_FOURCC( 'Y', 'U', 'Y', 'V')
266 *pi_chroma = i_chroma;
268 /* Create an image for surface extraction */
269 if( vaCreateImage( p_va->p_display, &fmt, i_width, i_height, &p_va->image ) )
271 p_va->image.image_id = 0;
275 /* Setup the ffmpeg hardware context */
276 *pp_hw_ctx = &p_va->hw_ctx;
278 memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) );
279 p_va->hw_ctx.display = p_va->p_display;
280 p_va->hw_ctx.config_id = p_va->i_config_id;
281 p_va->hw_ctx.context_id = p_va->i_context_id;
284 p_va->i_surface_chroma = i_chroma;
285 p_va->i_surface_width = i_width;
286 p_va->i_surface_height = i_height;
290 DestroySurfaces( p_va );
294 static int Setup( vlc_va_t *p_external, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
295 int i_width, int i_height )
297 vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
299 if( p_va->i_surface_width == i_width &&
300 p_va->i_surface_height == i_height )
302 *pp_hw_ctx = &p_va->hw_ctx;
303 *pi_chroma = p_va->i_surface_chroma;
309 if( p_va->i_surface_width || p_va->i_surface_height )
310 DestroySurfaces( p_va );
312 if( i_width > 0 && i_height > 0 )
313 return CreateSurfaces( p_va, pp_hw_ctx, pi_chroma, i_width, i_height );
317 static int Extract( vlc_va_t *p_external, picture_t *p_picture, AVFrame *p_ff )
319 vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
321 VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
323 #if VA_CHECK_VERSION(0,31,0)
324 if( vaSyncSurface( p_va->p_display, i_surface_id ) )
326 if( vaSyncSurface( p_va->p_display, p_va->i_context_id, i_surface_id ) )
330 /* XXX vaDeriveImage may be better but it is not supported by
334 if( vaGetImage( p_va->p_display, i_surface_id,
335 0, 0, p_va->i_surface_width, p_va->i_surface_height,
336 p_va->image.image_id) )
340 if( vaMapBuffer( p_va->p_display, p_va->image.buf, &p_base ) )
343 for( int i_plane = 0; i_plane < p_picture->i_planes; i_plane++ )
345 const int i_src_plane = ((p_va->image.format.fourcc == VA_FOURCC('Y','V','1','2' )) && i_plane != 0) ? (3 - i_plane) : i_plane;
346 const uint8_t *p_src = (uint8_t*)p_base + p_va->image.offsets[i_src_plane];
347 const int i_src_stride = p_va->image.pitches[i_src_plane];
349 uint8_t *p_dst = p_picture->p[i_plane].p_pixels;
350 const int i_dst_stride = p_picture->p[i_plane].i_pitch;
352 if( i_src_stride != i_dst_stride )
354 for( int i = 0; i < p_picture->p[i_plane].i_visible_lines; i++ )
356 vlc_memcpy( p_dst, p_src, __MIN( i_src_stride, i_dst_stride ) );
357 p_src += i_src_stride;
358 p_dst += i_dst_stride;
363 vlc_memcpy( p_dst, p_src, p_picture->p[i_plane].i_visible_lines * i_src_stride );
367 if( vaUnmapBuffer( p_va->p_display, p_va->image.buf ) )
372 static int Get( vlc_va_t *p_external, AVFrame *p_ff )
374 vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
378 /* Grab an unused surface, in case none are, try the oldest
379 * XXX using the oldest is a workaround in case a problem happens with ffmpeg */
380 for( i = 0, i_old = 0; i < p_va->i_surface_count; i++ )
382 vlc_va_surface_t *p_surface = &p_va->p_surface[i];
384 if( !p_surface->i_refcount )
387 if( p_surface->i_order < p_va->p_surface[i_old].i_order )
390 if( i >= p_va->i_surface_count )
393 vlc_va_surface_t *p_surface = &p_va->p_surface[i];
395 p_surface->i_refcount = 1;
396 p_surface->i_order = p_va->i_surface_order++;
399 for( int i = 0; i < 4; i++ )
401 p_ff->data[i] = NULL;
402 p_ff->linesize[i] = 0;
404 if( i == 0 || i == 3 )
405 p_ff->data[i] = (void*)(uintptr_t)p_surface->i_id;/* Yummie */
409 static void Release( vlc_va_t *p_external, AVFrame *p_ff )
411 vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
413 VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
415 for( int i = 0; i < p_va->i_surface_count; i++ )
417 vlc_va_surface_t *p_surface = &p_va->p_surface[i];
419 if( p_surface->i_id == i_surface_id )
420 p_surface->i_refcount--;
424 static void Close( vlc_va_vaapi_t *p_va )
426 if( p_va->i_surface_width || p_va->i_surface_height )
427 DestroySurfaces( p_va );
429 if( p_va->i_config_id )
430 vaDestroyConfig( p_va->p_display, p_va->i_config_id );
431 if( p_va->p_display )
432 vaTerminate( p_va->p_display );
433 if( p_va->p_display_x11 )
434 XCloseDisplay( p_va->p_display_x11 );
436 static void Delete( vlc_va_t *p_external )
438 vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
440 free( p_va->va.description );
445 vlc_va_t *vlc_va_NewVaapi( int i_codec_id )
447 if( !XInitThreads() )
450 vlc_va_vaapi_t *p_va = calloc( 1, sizeof(*p_va) );
454 if( Open( p_va, i_codec_id ) )
461 p_va->va.setup = Setup;
463 p_va->va.release = Release;
464 p_va->va.extract = Extract;
465 p_va->va.close = Delete;
469 vlc_va_t *vlc_va_NewVaapi( int i_codec_id )
471 VLC_UNUSED( i_codec_id );