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 # ifdef HAVE_AVCODEC_VAAPI
35 # include <libavcodec/vaapi.h>
37 #elif defined(HAVE_FFMPEG_AVCODEC_H)
38 # include <ffmpeg/avcodec.h>
46 #ifdef HAVE_AVCODEC_VAAPI
49 #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 int VaOpen( vlc_va_t
*p_va
, int i_codec_id
);
89 static void VaClose( vlc_va_t
*p_va
);
91 static int VaCreateSurfaces( vlc_va_t
*p_va
, void **pp_hw_ctx
, vlc_fourcc_t
*pi_chroma
,
92 int i_width
, int i_height
);
93 static void VaDestroySurfaces( vlc_va_t
*p_va
);
95 vlc_va_t
*VaNew( int i_codec_id
)
97 vlc_va_t
*p_va
= calloc( 1, sizeof(*p_va
) );
101 if( VaOpen( p_va
, i_codec_id
) )
108 void VaDelete( vlc_va_t
*p_va
)
113 int VaSetup( vlc_va_t
*p_va
, void **pp_hw_ctx
, vlc_fourcc_t
*pi_chroma
,
114 int i_width
, int i_height
)
116 if( p_va
->i_surface_width
== i_width
&&
117 p_va
->i_surface_height
== i_height
)
122 if( p_va
->i_surface_width
|| p_va
->i_surface_height
)
123 VaDestroySurfaces( p_va
);
125 if( i_width
> 0 && i_height
> 0 )
126 return VaCreateSurfaces( p_va
, pp_hw_ctx
, pi_chroma
, i_width
, i_height
);
130 void VaVersion( vlc_va_t
*p_va
, char *psz_version
, size_t i_version
)
132 snprintf( psz_version
, i_version
, "%d.%d", p_va
->i_version_major
, p_va
->i_version_minor
);
135 static int VaOpen( vlc_va_t
*p_va
, int i_codec_id
)
143 case CODEC_ID_MPEG1VIDEO
:
144 case CODEC_ID_MPEG2VIDEO
:
145 i_profile
= VAProfileMPEG2Main
;
146 i_surface_count
= 2+1;
149 i_profile
= VAProfileMPEG4AdvancedSimple
;
150 i_surface_count
= 2+1;
153 i_profile
= VAProfileVC1Main
;
154 i_surface_count
= 2+1;
157 i_profile
= VAProfileVC1Advanced
;
158 i_surface_count
= 2+1;
161 i_profile
= VAProfileH264High
;
162 i_surface_count
= 16+1;
169 memset( p_va
, 0, sizeof(*p_va
) );
171 /* Create a VA display */
172 p_va
->p_display_x11
= XOpenDisplay(NULL
);
173 if( !p_va
->p_display_x11
)
176 p_va
->p_display
= vaGetDisplay( p_va
->p_display_x11
);
177 if( !p_va
->p_display
)
180 if( vaInitialize( p_va
->p_display
, &p_va
->i_version_major
, &p_va
->i_version_minor
) )
183 /* Create a VA configuration */
184 VAConfigAttrib attrib
;
185 memset( &attrib
, 0, sizeof(attrib
) );
186 attrib
.type
= VAConfigAttribRTFormat
;
187 if( vaGetConfigAttributes( p_va
->p_display
,
188 i_profile
, VAEntrypointVLD
, &attrib
, 1 ) )
191 /* Not sure what to do if not, I don't have a way to test */
192 if( (attrib
.value
& VA_RT_FORMAT_YUV420
) == 0 )
194 if( vaCreateConfig( p_va
->p_display
,
195 i_profile
, VAEntrypointVLD
, &attrib
, 1, &p_va
->i_config_id
) )
197 p_va
->i_config_id
= 0;
201 p_va
->i_surface_count
= i_surface_count
;
208 static void VaClose( vlc_va_t
*p_va
)
210 if( p_va
->i_surface_width
|| p_va
->i_surface_height
)
211 VaDestroySurfaces( p_va
);
213 if( p_va
->i_config_id
)
214 vaDestroyConfig( p_va
->p_display
, p_va
->i_config_id
);
215 if( p_va
->p_display
)
216 vaTerminate( p_va
->p_display
);
217 if( p_va
->p_display_x11
)
218 XCloseDisplay( p_va
->p_display_x11
);
221 static int VaCreateSurfaces( vlc_va_t
*p_va
, void **pp_hw_ctx
, vlc_fourcc_t
*pi_chroma
,
222 int i_width
, int i_height
)
224 assert( i_width
> 0 && i_height
> 0 );
227 p_va
->p_surface
= calloc( p_va
->i_surface_count
, sizeof(*p_va
->p_surface
) );
228 if( !p_va
->p_surface
)
231 /* Create surfaces */
232 VASurfaceID pi_surface_id
[p_va
->i_surface_count
];
233 if( vaCreateSurfaces( p_va
->p_display
, i_width
, i_height
, VA_RT_FORMAT_YUV420
,
234 p_va
->i_surface_count
, pi_surface_id
) )
236 for( int i
= 0; i
< p_va
->i_surface_count
; i
++ )
237 p_va
->p_surface
[i
].i_id
= VA_INVALID_SURFACE
;
241 for( int i
= 0; i
< p_va
->i_surface_count
; i
++ )
243 vlc_va_surface_t
*p_surface
= &p_va
->p_surface
[i
];
245 p_surface
->i_id
= pi_surface_id
[i
];
246 p_surface
->i_refcount
= 0;
247 p_surface
->i_order
= 0;
250 /* Create a context */
251 if( vaCreateContext( p_va
->p_display
, p_va
->i_config_id
,
252 i_width
, i_height
, VA_PROGRESSIVE
,
253 pi_surface_id
, p_va
->i_surface_count
, &p_va
->i_context_id
) )
255 p_va
->i_context_id
= 0;
259 /* Find a supported image chroma */
260 int i_fmt_count
= vaMaxNumImageFormats( p_va
->p_display
);
261 VAImageFormat
*p_fmt
= calloc( i_fmt_count
, sizeof(*p_fmt
) );
265 if( vaQueryImageFormats( p_va
->p_display
, p_fmt
, &i_fmt_count
) )
271 vlc_fourcc_t i_chroma
= 0;
273 for( int i
= 0; i
< i_fmt_count
; i
++ )
275 if( p_fmt
[i
].fourcc
== VA_FOURCC( 'Y', 'V', '1', '2' ) ||
276 p_fmt
[i
].fourcc
== VA_FOURCC( 'I', '4', '2', '0' ) )
278 i_chroma
= VLC_FOURCC( 'I', '4', '2', '0' );
281 /* TODO: It seems that these may also be available (but not
283 * VA_FOURCC( 'N', 'V', '1', '2')
284 * VA_FOURCC( 'U', 'Y', 'V', 'Y')
285 * VA_FOURCC( 'Y', 'U', 'Y', 'V')
291 *pi_chroma
= i_chroma
;
293 /* Create an image for surface extraction */
294 if( vaCreateImage( p_va
->p_display
, &fmt
, i_width
, i_height
, &p_va
->image
) )
296 p_va
->image
.image_id
= 0;
300 /* Setup the ffmpeg hardware context */
301 *pp_hw_ctx
= &p_va
->hw_ctx
;
303 memset( &p_va
->hw_ctx
, 0, sizeof(p_va
->hw_ctx
) );
304 p_va
->hw_ctx
.display
= p_va
->p_display
;
305 p_va
->hw_ctx
.config_id
= p_va
->i_config_id
;
306 p_va
->hw_ctx
.context_id
= p_va
->i_context_id
;
309 p_va
->i_surface_chroma
= i_chroma
;
310 p_va
->i_surface_width
= i_width
;
311 p_va
->i_surface_height
= i_height
;
315 VaDestroySurfaces( p_va
);
318 static void VaDestroySurfaces( vlc_va_t
*p_va
)
320 if( p_va
->image
.image_id
)
321 vaDestroyImage( p_va
->p_display
, p_va
->image
.image_id
);
323 if( p_va
->i_context_id
)
324 vaDestroyContext( p_va
->p_display
, p_va
->i_context_id
);
326 for( int i
= 0; i
< p_va
->i_surface_count
&& p_va
->p_surface
; i
++ )
328 vlc_va_surface_t
*p_surface
= &p_va
->p_surface
[i
];
330 if( p_surface
->i_id
!= VA_INVALID_SURFACE
)
331 vaDestroySurfaces( p_va
->p_display
, &p_surface
->i_id
, 1 );
333 free( p_va
->p_surface
);
336 p_va
->image
.image_id
= 0;
337 p_va
->i_context_id
= 0;
338 p_va
->p_surface
= NULL
;
339 p_va
->i_surface_width
= 0;
340 p_va
->i_surface_height
= 0;
343 int VaExtract( vlc_va_t
*p_va
, picture_t
*p_picture
, AVFrame
*p_ff
)
345 VASurfaceID i_surface_id
= (VASurfaceID
)(uintptr_t)p_ff
->data
[3];
347 #if VA_CHECK_VERSION(0,31,0)
348 if( vaSyncSurface( p_va
->p_display
, i_surface_id
) )
350 if( vaSyncSurface( p_va
->p_display
, p_va
->i_context_id
, i_surface_id
) )
354 /* XXX vaDeriveImage may be better but it is not supported by
358 if( vaGetImage( p_va
->p_display
, i_surface_id
,
359 0, 0, p_va
->i_surface_width
, p_va
->i_surface_height
,
360 p_va
->image
.image_id
) )
364 if( vaMapBuffer( p_va
->p_display
, p_va
->image
.buf
, &p_base
) )
367 for( int i_plane
= 0; i_plane
< p_picture
->i_planes
; i_plane
++ )
369 const int i_src_plane
= ((p_va
->image
.format
.fourcc
== VA_FOURCC('Y','V','1','2' )) && i_plane
!= 0) ? (3 - i_plane
) : i_plane
;
370 const uint8_t *p_src
= (uint8_t*)p_base
+ p_va
->image
.offsets
[i_src_plane
];
371 const int i_src_stride
= p_va
->image
.pitches
[i_src_plane
];
373 uint8_t *p_dst
= p_picture
->p
[i_plane
].p_pixels
;
374 const int i_dst_stride
= p_picture
->p
[i_plane
].i_pitch
;
376 if( i_src_stride
!= i_dst_stride
)
378 for( int i
= 0; i
< p_picture
->p
[i_plane
].i_visible_lines
; i
++ )
380 vlc_memcpy( p_dst
, p_src
, __MIN( i_src_stride
, i_dst_stride
) );
381 p_src
+= i_src_stride
;
382 p_dst
+= i_dst_stride
;
387 vlc_memcpy( p_dst
, p_src
, p_picture
->p
[i_plane
].i_visible_lines
* i_src_stride
);
391 if( vaUnmapBuffer( p_va
->p_display
, p_va
->image
.buf
) )
396 int VaGrabSurface( vlc_va_t
*p_va
, AVFrame
*p_ff
)
401 /* Grab an unused surface, in case none are, try the oldest
402 * XXX using the oldest is a workaround in case a problem happens with ffmpeg */
403 for( i
= 0, i_old
= 0; i
< p_va
->i_surface_count
; i
++ )
405 vlc_va_surface_t
*p_surface
= &p_va
->p_surface
[i
];
407 if( !p_surface
->i_refcount
)
410 if( p_surface
->i_order
< p_va
->p_surface
[i_old
].i_order
)
413 if( i
>= p_va
->i_surface_count
)
416 vlc_va_surface_t
*p_surface
= &p_va
->p_surface
[i
];
418 p_surface
->i_refcount
= 1;
419 p_surface
->i_order
= p_va
->i_surface_order
++;
422 for( int i
= 0; i
< 4; i
++ )
424 p_ff
->data
[i
] = NULL
;
425 p_ff
->linesize
[i
] = 0;
427 if( i
== 0 || i
== 3 )
428 p_ff
->data
[i
] = (void*)(uintptr_t)p_surface
->i_id
;/* Yummie */
432 void VaUngrabSurface( vlc_va_t
*p_va
, AVFrame
*p_ff
)
434 VASurfaceID i_surface_id
= (VASurfaceID
)(uintptr_t)p_ff
->data
[3];
436 for( int i
= 0; i
< p_va
->i_surface_count
; i
++ )
438 vlc_va_surface_t
*p_surface
= &p_va
->p_surface
[i
];
440 if( p_surface
->i_id
== i_surface_id
)
441 p_surface
->i_refcount
--;
447 vlc_va_t
*VaNew( int i_codec_id
)
449 VLC_UNUSED(i_codec_id
);
452 void VaDelete( vlc_va_t
*p_va
)
458 void VaVersion( vlc_va_t
*p_va
, char *psz_version
, size_t i_version
)
460 VLC_UNUSED(p_va
); VLC_UNUSED(psz_version
); VLC_UNUSED(i_version
);
464 int VaSetup( vlc_va_t
*p_va
, void **pp_hw_ctx
, vlc_fourcc_t
*pi_chroma
,
465 int i_width
, int i_height
)
467 VLC_UNUSED(p_va
); VLC_UNUSED(pp_hw_ctx
); VLC_UNUSED(pi_chroma
);
468 VLC_UNUSED(i_width
); VLC_UNUSED(i_height
);
473 int VaExtract( vlc_va_t
*p_va
, picture_t
*p_picture
, AVFrame
*p_ff
)
475 VLC_UNUSED(p_va
); VLC_UNUSED(p_picture
); VLC_UNUSED(p_ff
);
480 int VaGrabSurface( vlc_va_t
*p_va
, AVFrame
*p_ff
)
482 VLC_UNUSED(p_va
); VLC_UNUSED(p_ff
);
487 void VaUngrabSurface( vlc_va_t
*p_va
, AVFrame
*p_ff
)
489 VLC_UNUSED(p_va
); VLC_UNUSED(p_ff
);