]> git.sesse.net Git - vlc/blob - modules/codec/avcodec/vaapi.c
Used AVCodecContext::ticks_per_frame when available.
[vlc] / modules / codec / avcodec / vaapi.c
1 /*****************************************************************************
2  * vaapi.c: VAAPI helpers for the ffmpeg decoder
3  *****************************************************************************
4  * Copyright (C) 2009 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir_AT_ videolan _DOT_ org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc_common.h>
29 #include <vlc_vout.h>
30 #include <assert.h>
31
32 #ifdef HAVE_LIBAVCODEC_AVCODEC_H
33 #   include <libavcodec/avcodec.h>
34 #   ifdef HAVE_AVCODEC_VAAPI
35 #       include <libavcodec/vaapi.h>
36 #   endif
37 #elif defined(HAVE_FFMPEG_AVCODEC_H)
38 #   include <ffmpeg/avcodec.h>
39 #else
40 #   include <avcodec.h>
41 #endif
42
43 #include "avcodec.h"
44 #include "vaapi.h"
45
46 #ifdef HAVE_AVCODEC_VAAPI
47
48 #include <X11/Xlib.h>
49 #include <va/va_x11.h>
50
51
52 typedef struct
53 {
54     VASurfaceID  i_id;
55     int          i_refcount;
56     unsigned int i_order;
57
58 } vlc_va_surface_t;
59
60 struct vlc_va_t
61 {
62     /* */
63     Display      *p_display_x11;
64     VADisplay     p_display;
65
66     VAConfigID    i_config_id;
67     VAContextID   i_context_id;
68
69     struct vaapi_context hw_ctx;
70
71     /* */
72     int i_version_major;
73     int i_version_minor;
74
75     /* */
76     int          i_surface_count;
77     unsigned int i_surface_order;
78     int          i_surface_width;
79     int          i_surface_height;
80     vlc_fourcc_t i_surface_chroma;
81
82     vlc_va_surface_t *p_surface;
83
84     VAImage      image;
85
86 };
87
88 static int VaOpen( vlc_va_t *p_va, int i_codec_id );
89 static void VaClose( vlc_va_t *p_va );
90
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 );
94
95 vlc_va_t *VaNew( int i_codec_id )
96 {
97     vlc_va_t *p_va = calloc( 1, sizeof(*p_va) );
98     if( !p_va )
99         return NULL;
100
101     if( VaOpen( p_va, i_codec_id ) )
102     {
103         free( p_va );
104         return NULL;
105     }
106     return p_va;
107 }
108 void VaDelete( vlc_va_t *p_va )
109 {
110     VaClose( p_va );
111     free( p_va );
112 }
113 int VaSetup( vlc_va_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
114              int i_width, int i_height )
115 {
116     if( p_va->i_surface_width == i_width &&
117         p_va->i_surface_height == i_height )
118         return VLC_SUCCESS;
119
120     *pp_hw_ctx = NULL;
121     *pi_chroma = 0;
122     if( p_va->i_surface_width || p_va->i_surface_height )
123         VaDestroySurfaces( p_va );
124
125     if( i_width > 0 && i_height > 0 )
126         return VaCreateSurfaces( p_va, pp_hw_ctx, pi_chroma, i_width, i_height );
127
128     return VLC_EGENERIC;
129 }
130 void VaVersion( vlc_va_t *p_va, char *psz_version, size_t i_version )
131 {
132     snprintf( psz_version, i_version, "%d.%d", p_va->i_version_major, p_va->i_version_minor );
133 }
134
135 static int VaOpen( vlc_va_t *p_va, int i_codec_id )
136 {
137     VAProfile i_profile;
138     int i_surface_count;
139
140     /* */
141     switch( i_codec_id )
142     {
143     case CODEC_ID_MPEG1VIDEO:
144     case CODEC_ID_MPEG2VIDEO:
145         i_profile = VAProfileMPEG2Main;
146         i_surface_count = 2+1;
147         break;
148     case CODEC_ID_MPEG4:
149         i_profile = VAProfileMPEG4AdvancedSimple;
150         i_surface_count = 2+1;
151         break;
152     case CODEC_ID_WMV3:
153         i_profile = VAProfileVC1Main;
154         i_surface_count = 2+1;
155         break;
156     case CODEC_ID_VC1:
157         i_profile = VAProfileVC1Advanced;
158         i_surface_count = 2+1;
159         break;
160     case CODEC_ID_H264:
161         i_profile = VAProfileH264High;
162         i_surface_count = 16+1;
163         break;
164     default:
165         return VLC_EGENERIC;
166     }
167
168     /* */
169     memset( p_va, 0, sizeof(*p_va) );
170
171     /* Create a VA display */
172     p_va->p_display_x11 = XOpenDisplay(NULL);
173     if( !p_va->p_display_x11 )
174         goto error;
175
176     p_va->p_display = vaGetDisplay( p_va->p_display_x11 );
177     if( !p_va->p_display )
178         goto error;
179
180     if( vaInitialize( p_va->p_display, &p_va->i_version_major, &p_va->i_version_minor ) )
181         goto error;
182
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 ) )
189         goto error;
190
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 )
193         goto error;
194     if( vaCreateConfig( p_va->p_display,
195                         i_profile, VAEntrypointVLD, &attrib, 1, &p_va->i_config_id ) )
196     {
197         p_va->i_config_id = 0;
198         goto error;
199     }
200
201     p_va->i_surface_count = i_surface_count;
202
203     return VLC_SUCCESS;
204
205 error:
206     return VLC_EGENERIC;
207 }
208 static void VaClose( vlc_va_t *p_va )
209 {
210     if( p_va->i_surface_width || p_va->i_surface_height )
211         VaDestroySurfaces( p_va );
212
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 );
219 }
220
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 )
223 {
224     assert( i_width > 0 && i_height > 0 );
225
226     /* */
227     p_va->p_surface = calloc( p_va->i_surface_count, sizeof(*p_va->p_surface) );
228     if( !p_va->p_surface )
229         return VLC_EGENERIC;
230
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 ) )
235     {
236         for( int i = 0; i < p_va->i_surface_count; i++ )
237             p_va->p_surface[i].i_id = VA_INVALID_SURFACE;
238         goto error;
239     }
240
241     for( int i = 0; i < p_va->i_surface_count; i++ )
242     {
243         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
244
245         p_surface->i_id = pi_surface_id[i];
246         p_surface->i_refcount = 0;
247         p_surface->i_order = 0;
248     }
249
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 ) )
254     {
255         p_va->i_context_id = 0;
256         goto error;
257     }
258
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) );
262     if( !p_fmt )
263         goto error;
264
265     if( vaQueryImageFormats( p_va->p_display, p_fmt, &i_fmt_count ) )
266     {
267         free( p_fmt );
268         goto error;
269     }
270
271     vlc_fourcc_t  i_chroma = 0;
272     VAImageFormat fmt;
273     for( int i = 0; i < i_fmt_count; i++ )
274     {
275         if( p_fmt[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) ||
276             p_fmt[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) )
277         {
278             i_chroma = VLC_FOURCC( 'I', '4', '2', '0' );
279             fmt = p_fmt[i];
280         }
281         /* TODO: It seems that these may also be available (but not
282          * with my setup):
283          * VA_FOURCC( 'N', 'V', '1', '2')
284          * VA_FOURCC( 'U', 'Y', 'V', 'Y')
285          * VA_FOURCC( 'Y', 'U', 'Y', 'V')
286          */
287     }
288     free( p_fmt );
289     if( !i_chroma )
290         goto error;
291     *pi_chroma = i_chroma;
292
293     /* Create an image for surface extraction */
294     if( vaCreateImage(  p_va->p_display, &fmt, i_width, i_height, &p_va->image ) )
295     {
296         p_va->image.image_id = 0;
297         goto error;
298     }
299
300     /* Setup the ffmpeg hardware context */
301     *pp_hw_ctx = &p_va->hw_ctx;
302
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;
307
308     /* */
309     p_va->i_surface_chroma = i_chroma;
310     p_va->i_surface_width = i_width;
311     p_va->i_surface_height = i_height;
312     return VLC_SUCCESS;
313
314 error:
315     VaDestroySurfaces( p_va );
316     return VLC_EGENERIC;
317 }
318 static void VaDestroySurfaces( vlc_va_t *p_va )
319 {
320     if( p_va->image.image_id )
321         vaDestroyImage( p_va->p_display, p_va->image.image_id );
322
323     if( p_va->i_context_id )
324         vaDestroyContext( p_va->p_display, p_va->i_context_id );
325
326     for( int i = 0; i < p_va->i_surface_count && p_va->p_surface; i++ )
327     {
328         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
329
330         if( p_surface->i_id != VA_INVALID_SURFACE )
331             vaDestroySurfaces( p_va->p_display, &p_surface->i_id, 1 );
332     }
333     free( p_va->p_surface );
334
335     /* */
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;
341 }
342
343 int VaExtract( vlc_va_t *p_va, picture_t *p_picture, AVFrame *p_ff )
344 {
345     VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
346
347     if( vaSyncSurface( p_va->p_display, p_va->i_context_id, i_surface_id ) )
348         return VLC_EGENERIC;
349
350     /* XXX vaDeriveImage may be better but it is not supported by
351      * my setup.
352      */
353
354     if( vaGetImage( p_va->p_display, i_surface_id,
355                     0, 0, p_va->i_surface_width, p_va->i_surface_height,
356                     p_va->image.image_id) )
357         return VLC_EGENERIC;
358
359     void *p_base;
360     if( vaMapBuffer( p_va->p_display, p_va->image.buf, &p_base ) )
361         return VLC_EGENERIC;
362
363     for( int i_plane = 0; i_plane < p_picture->i_planes; i_plane++ )
364     {
365         const int i_src_plane = ((p_va->image.format.fourcc == VA_FOURCC('Y','V','1','2' )) && i_plane != 0) ?  (3 - i_plane) : i_plane;
366         const uint8_t *p_src = (uint8_t*)p_base + p_va->image.offsets[i_src_plane];
367         const int i_src_stride = p_va->image.pitches[i_src_plane];
368
369         uint8_t *p_dst = p_picture->p[i_plane].p_pixels;
370         const int i_dst_stride = p_picture->p[i_plane].i_pitch;
371
372         if( i_src_stride != i_dst_stride )
373         {
374             for( int i = 0; i < p_picture->p[i_plane].i_visible_lines; i++ )
375             {
376                 vlc_memcpy( p_dst, p_src, __MIN( i_src_stride, i_dst_stride ) );
377                 p_src += i_src_stride;
378                 p_dst += i_dst_stride;
379             }
380         }
381         else
382         {
383             vlc_memcpy( p_dst, p_src, p_picture->p[i_plane].i_visible_lines * i_src_stride );
384         }
385     }
386
387     if( vaUnmapBuffer( p_va->p_display, p_va->image.buf ) )
388         return VLC_EGENERIC;
389
390     return VLC_SUCCESS;
391 }
392 int VaGrabSurface( vlc_va_t *p_va, AVFrame *p_ff )
393 {
394     int i_old;
395     int i;
396
397     /* Grab an unused surface, in case none are, try the oldest
398      * XXX using the oldest is a workaround in case a problem happens with ffmpeg */
399     for( i = 0, i_old = 0; i < p_va->i_surface_count; i++ )
400     {
401         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
402
403         if( !p_surface->i_refcount )
404             break;
405
406         if( p_surface->i_order < p_va->p_surface[i_old].i_order )
407             i_old = i;
408     }
409     if( i >= p_va->i_surface_count )
410         i = i_old;
411
412     vlc_va_surface_t *p_surface = &p_va->p_surface[i];
413
414     p_surface->i_refcount = 1;
415     p_surface->i_order = p_va->i_surface_order++;
416
417     /* */
418     for( int i = 0; i < 4; i++ )
419     {
420         p_ff->data[i] = NULL;
421         p_ff->linesize[i] = 0;
422
423         if( i == 0 || i == 3 )
424             p_ff->data[i] = (void*)(uintptr_t)p_surface->i_id;/* Yummie */
425     }
426     return VLC_SUCCESS;
427 }
428 void VaUngrabSurface( vlc_va_t *p_va, AVFrame *p_ff )
429 {
430     VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
431
432     for( int i = 0; i < p_va->i_surface_count; i++ )
433     {
434         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
435
436         if( p_surface->i_id == i_surface_id )
437             p_surface->i_refcount--;
438     }
439 }
440
441 #else
442
443 vlc_va_t *VaNew( int i_codec_id )
444 {
445     VLC_UNUSED(i_codec_id);
446     return NULL;
447 }
448 void VaDelete( vlc_va_t *p_va )
449 {
450     VLC_UNUSED(p_va);
451     assert( 0 );
452 }
453
454 void VaVersion( vlc_va_t *p_va, char *psz_version, size_t i_version )
455 {
456     VLC_UNUSED(p_va); VLC_UNUSED(psz_version); VLC_UNUSED(i_version);
457     assert(0);
458 }
459
460 int VaSetup( vlc_va_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
461              int i_width, int i_height )
462 {
463     VLC_UNUSED(p_va); VLC_UNUSED(pp_hw_ctx); VLC_UNUSED(pi_chroma);
464     VLC_UNUSED(i_width); VLC_UNUSED(i_height);
465     assert(0);
466     return -1;
467 }
468
469 int VaExtract( vlc_va_t *p_va, picture_t *p_picture, AVFrame *p_ff )
470 {
471     VLC_UNUSED(p_va); VLC_UNUSED(p_picture); VLC_UNUSED(p_ff);
472     assert(0);
473     return -1;
474 }
475
476 int VaGrabSurface( vlc_va_t *p_va, AVFrame *p_ff )
477 {
478     VLC_UNUSED(p_va); VLC_UNUSED(p_ff);
479     assert(0);
480     return -1;
481 }
482
483 void VaUngrabSurface( vlc_va_t *p_va, AVFrame *p_ff )
484 {
485     VLC_UNUSED(p_va); VLC_UNUSED(p_ff);
486     assert(0);
487 }
488
489 #endif