]> git.sesse.net Git - vlc/blob - modules/codec/avcodec/vaapi.c
Added logs about direct rendering usage (avcodec).
[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 #elif defined(HAVE_FFMPEG_AVCODEC_H)
35 #   include <ffmpeg/avcodec.h>
36 #else
37 #   include <avcodec.h>
38 #endif
39
40 #include "avcodec.h"
41 #include "va.h"
42
43 #ifdef HAVE_AVCODEC_VAAPI
44
45 #include <libavcodec/vaapi.h>
46
47 #include <X11/Xlib.h>
48 #include <va/va_x11.h>
49
50 typedef struct
51 {
52     VASurfaceID  i_id;
53     int          i_refcount;
54     unsigned int i_order;
55
56 } vlc_va_surface_t;
57
58 typedef struct
59 {
60     vlc_va_t     va;
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 } vlc_va_vaapi_t;
87
88 static vlc_va_vaapi_t *vlc_va_vaapi_Get( void *p_va )
89 {
90     return p_va;
91 }
92
93 /* */
94 static int Open( vlc_va_vaapi_t *p_va, int i_codec_id )
95 {
96     VAProfile i_profile;
97     int i_surface_count;
98
99     /* */
100     switch( i_codec_id )
101     {
102     case CODEC_ID_MPEG1VIDEO:
103     case CODEC_ID_MPEG2VIDEO:
104         i_profile = VAProfileMPEG2Main;
105         i_surface_count = 2+1;
106         break;
107     case CODEC_ID_MPEG4:
108         i_profile = VAProfileMPEG4AdvancedSimple;
109         i_surface_count = 2+1;
110         break;
111     case CODEC_ID_WMV3:
112         i_profile = VAProfileVC1Main;
113         i_surface_count = 2+1;
114         break;
115     case CODEC_ID_VC1:
116         i_profile = VAProfileVC1Advanced;
117         i_surface_count = 2+1;
118         break;
119     case CODEC_ID_H264:
120         i_profile = VAProfileH264High;
121         i_surface_count = 16+1;
122         break;
123     default:
124         return VLC_EGENERIC;
125     }
126
127     /* */
128     memset( p_va, 0, sizeof(*p_va) );
129
130     /* Create a VA display */
131     p_va->p_display_x11 = XOpenDisplay(NULL);
132     if( !p_va->p_display_x11 )
133         goto error;
134
135     p_va->p_display = vaGetDisplay( p_va->p_display_x11 );
136     if( !p_va->p_display )
137         goto error;
138
139     if( vaInitialize( p_va->p_display, &p_va->i_version_major, &p_va->i_version_minor ) )
140         goto error;
141
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 ) )
148         goto error;
149
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 )
152         goto error;
153     if( vaCreateConfig( p_va->p_display,
154                         i_profile, VAEntrypointVLD, &attrib, 1, &p_va->i_config_id ) )
155     {
156         p_va->i_config_id = 0;
157         goto error;
158     }
159
160     p_va->i_surface_count = i_surface_count;
161
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;
165
166     return VLC_SUCCESS;
167
168 error:
169     return VLC_EGENERIC;
170 }
171
172 static void DestroySurfaces( vlc_va_vaapi_t *p_va )
173 {
174     if( p_va->image.image_id )
175         vaDestroyImage( p_va->p_display, p_va->image.image_id );
176
177     if( p_va->i_context_id )
178         vaDestroyContext( p_va->p_display, p_va->i_context_id );
179
180     for( int i = 0; i < p_va->i_surface_count && p_va->p_surface; i++ )
181     {
182         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
183
184         if( p_surface->i_id != VA_INVALID_SURFACE )
185             vaDestroySurfaces( p_va->p_display, &p_surface->i_id, 1 );
186     }
187     free( p_va->p_surface );
188
189     /* */
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;
195 }
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 )
198 {
199     assert( i_width > 0 && i_height > 0 );
200
201     /* */
202     p_va->p_surface = calloc( p_va->i_surface_count, sizeof(*p_va->p_surface) );
203     if( !p_va->p_surface )
204         return VLC_EGENERIC;
205
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 ) )
210     {
211         for( int i = 0; i < p_va->i_surface_count; i++ )
212             p_va->p_surface[i].i_id = VA_INVALID_SURFACE;
213         goto error;
214     }
215
216     for( int i = 0; i < p_va->i_surface_count; i++ )
217     {
218         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
219
220         p_surface->i_id = pi_surface_id[i];
221         p_surface->i_refcount = 0;
222         p_surface->i_order = 0;
223     }
224
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 ) )
229     {
230         p_va->i_context_id = 0;
231         goto error;
232     }
233
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) );
237     if( !p_fmt )
238         goto error;
239
240     if( vaQueryImageFormats( p_va->p_display, p_fmt, &i_fmt_count ) )
241     {
242         free( p_fmt );
243         goto error;
244     }
245
246     vlc_fourcc_t  i_chroma = 0;
247     VAImageFormat fmt;
248     for( int i = 0; i < i_fmt_count; i++ )
249     {
250         if( p_fmt[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) ||
251             p_fmt[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) )
252         {
253             i_chroma = VLC_FOURCC( 'I', '4', '2', '0' );
254             fmt = p_fmt[i];
255         }
256         /* TODO: It seems that these may also be available (but not
257          * with my setup):
258          * VA_FOURCC( 'N', 'V', '1', '2')
259          * VA_FOURCC( 'U', 'Y', 'V', 'Y')
260          * VA_FOURCC( 'Y', 'U', 'Y', 'V')
261          */
262     }
263     free( p_fmt );
264     if( !i_chroma )
265         goto error;
266     *pi_chroma = i_chroma;
267
268     /* Create an image for surface extraction */
269     if( vaCreateImage(  p_va->p_display, &fmt, i_width, i_height, &p_va->image ) )
270     {
271         p_va->image.image_id = 0;
272         goto error;
273     }
274
275     /* Setup the ffmpeg hardware context */
276     *pp_hw_ctx = &p_va->hw_ctx;
277
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;
282
283     /* */
284     p_va->i_surface_chroma = i_chroma;
285     p_va->i_surface_width = i_width;
286     p_va->i_surface_height = i_height;
287     return VLC_SUCCESS;
288
289 error:
290     DestroySurfaces( p_va );
291     return VLC_EGENERIC;
292 }
293
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 )
296 {
297     vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
298
299     if( p_va->i_surface_width == i_width &&
300         p_va->i_surface_height == i_height )
301     {
302         *pp_hw_ctx = &p_va->hw_ctx;
303         *pi_chroma = p_va->i_surface_chroma;
304         return VLC_SUCCESS;
305     }
306
307     *pp_hw_ctx = NULL;
308     *pi_chroma = 0;
309     if( p_va->i_surface_width || p_va->i_surface_height )
310         DestroySurfaces( p_va );
311
312     if( i_width > 0 && i_height > 0 )
313         return CreateSurfaces( p_va, pp_hw_ctx, pi_chroma, i_width, i_height );
314
315     return VLC_EGENERIC;
316 }
317 static int Extract( vlc_va_t *p_external, picture_t *p_picture, AVFrame *p_ff )
318 {
319     vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
320
321     VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
322
323 #if VA_CHECK_VERSION(0,31,0)
324     if( vaSyncSurface( p_va->p_display, i_surface_id ) )
325 #else
326     if( vaSyncSurface( p_va->p_display, p_va->i_context_id, i_surface_id ) )
327 #endif
328         return VLC_EGENERIC;
329
330     /* XXX vaDeriveImage may be better but it is not supported by
331      * my setup.
332      */
333
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) )
337         return VLC_EGENERIC;
338
339     void *p_base;
340     if( vaMapBuffer( p_va->p_display, p_va->image.buf, &p_base ) )
341         return VLC_EGENERIC;
342
343     for( int i_plane = 0; i_plane < p_picture->i_planes; i_plane++ )
344     {
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];
348
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;
351
352         if( i_src_stride != i_dst_stride )
353         {
354             for( int i = 0; i < p_picture->p[i_plane].i_visible_lines; i++ )
355             {
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;
359             }
360         }
361         else
362         {
363             vlc_memcpy( p_dst, p_src, p_picture->p[i_plane].i_visible_lines * i_src_stride );
364         }
365     }
366
367     if( vaUnmapBuffer( p_va->p_display, p_va->image.buf ) )
368         return VLC_EGENERIC;
369
370     return VLC_SUCCESS;
371 }
372 static int Get( vlc_va_t *p_external, AVFrame *p_ff )
373 {
374     vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
375     int i_old;
376     int i;
377
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++ )
381     {
382         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
383
384         if( !p_surface->i_refcount )
385             break;
386
387         if( p_surface->i_order < p_va->p_surface[i_old].i_order )
388             i_old = i;
389     }
390     if( i >= p_va->i_surface_count )
391         i = i_old;
392
393     vlc_va_surface_t *p_surface = &p_va->p_surface[i];
394
395     p_surface->i_refcount = 1;
396     p_surface->i_order = p_va->i_surface_order++;
397
398     /* */
399     for( int i = 0; i < 4; i++ )
400     {
401         p_ff->data[i] = NULL;
402         p_ff->linesize[i] = 0;
403
404         if( i == 0 || i == 3 )
405             p_ff->data[i] = (void*)(uintptr_t)p_surface->i_id;/* Yummie */
406     }
407     return VLC_SUCCESS;
408 }
409 static void Release( vlc_va_t *p_external, AVFrame *p_ff )
410 {
411     vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
412
413     VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
414
415     for( int i = 0; i < p_va->i_surface_count; i++ )
416     {
417         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
418
419         if( p_surface->i_id == i_surface_id )
420             p_surface->i_refcount--;
421     }
422 }
423
424 static void Close( vlc_va_vaapi_t *p_va )
425 {
426     if( p_va->i_surface_width || p_va->i_surface_height )
427         DestroySurfaces( p_va );
428
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 );
435 }
436 static void Delete( vlc_va_t *p_external )
437 {
438     vlc_va_vaapi_t *p_va = vlc_va_vaapi_Get(p_external);
439     Close( p_va );
440     free( p_va->va.description );
441     free( p_va );
442 }
443
444 /* */
445 vlc_va_t *vlc_va_NewVaapi( int i_codec_id )
446 {
447     vlc_va_vaapi_t *p_va = calloc( 1, sizeof(*p_va) );
448     if( !p_va )
449         return NULL;
450
451     if( Open( p_va, i_codec_id ) )
452     {
453         free( p_va );
454         return NULL;
455     }
456
457     /* */
458     p_va->va.setup = Setup;
459     p_va->va.get = Get;
460     p_va->va.release = Release;
461     p_va->va.extract = Extract;
462     p_va->va.close = Delete;
463     return &p_va->va;
464 }
465 #else
466 vlc_va_t *vlc_va_NewVaapi( int i_codec_id )
467 {
468     VLC_UNUSED( i_codec_id );
469     return NULL;
470 }
471 #endif