]> git.sesse.net Git - vlc/blob - modules/codec/avcodec/vaapi.c
Use _WIN32 rather than WIN32 (same for WIN64)
[vlc] / modules / codec / avcodec / vaapi.c
1 /*****************************************************************************
2  * vaapi.c: VAAPI helpers for the libavcodec 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 it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * 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 <assert.h>
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_fourcc.h>
33 #include <vlc_xlib.h>
34
35 #include <libavcodec/avcodec.h>
36 #include <libavcodec/vaapi.h>
37 #include <X11/Xlib.h>
38 #include <va/va_x11.h>
39
40 #include "avcodec.h"
41 #include "va.h"
42 #include "copy.h"
43
44 static int Create( vlc_va_t *, int, const es_format_t * );
45 static void Delete( vlc_va_t * );
46
47 vlc_module_begin ()
48     set_description( N_("Video Acceleration (VA) API") )
49     set_capability( "hw decoder", 50 )
50     set_category( CAT_INPUT )
51     set_subcategory( SUBCAT_INPUT_VCODEC )
52     set_callbacks( Create, Delete )
53 vlc_module_end ()
54
55 typedef struct
56 {
57     VASurfaceID  i_id;
58     int          i_refcount;
59     unsigned int i_order;
60
61 } vlc_va_surface_t;
62
63 struct vlc_va_sys_t
64 {
65     Display      *p_display_x11;
66     VADisplay     p_display;
67
68     VAConfigID    i_config_id;
69     VAContextID   i_context_id;
70
71     struct vaapi_context hw_ctx;
72
73     /* */
74     int i_version_major;
75     int i_version_minor;
76
77     /* */
78     int          i_surface_count;
79     unsigned int i_surface_order;
80     int          i_surface_width;
81     int          i_surface_height;
82     vlc_fourcc_t i_surface_chroma;
83
84     vlc_va_surface_t *p_surface;
85
86     VAImage      image;
87     copy_cache_t image_cache;
88
89     bool b_supports_derive;
90 };
91
92 /* */
93 static int Open( vlc_va_t *p_external, int i_codec_id )
94 {
95     vlc_va_sys_t *p_va = calloc( 1, sizeof(*p_va) );
96     if ( unlikely(p_va == NULL) )
97        return VLC_ENOMEM;
98
99     VAProfile i_profile, *p_profiles_list;
100     bool b_supported_profile = false;
101     int i_profiles_nb = 0;
102     int i_surface_count;
103
104     /* */
105     switch( i_codec_id )
106     {
107     case AV_CODEC_ID_MPEG1VIDEO:
108     case AV_CODEC_ID_MPEG2VIDEO:
109         i_profile = VAProfileMPEG2Main;
110         i_surface_count = 2+1;
111         break;
112     case AV_CODEC_ID_MPEG4:
113         i_profile = VAProfileMPEG4AdvancedSimple;
114         i_surface_count = 2+1;
115         break;
116     case AV_CODEC_ID_WMV3:
117         i_profile = VAProfileVC1Main;
118         i_surface_count = 2+1;
119         break;
120     case AV_CODEC_ID_VC1:
121         i_profile = VAProfileVC1Advanced;
122         i_surface_count = 2+1;
123         break;
124     case AV_CODEC_ID_H264:
125         i_profile = VAProfileH264High;
126         i_surface_count = 16+1;
127         break;
128     default:
129         return VLC_EGENERIC;
130     }
131
132     /* */
133     p_va->i_config_id  = VA_INVALID_ID;
134     p_va->i_context_id = VA_INVALID_ID;
135     p_va->image.image_id = VA_INVALID_ID;
136
137     /* Create a VA display */
138     p_va->p_display_x11 = XOpenDisplay(NULL);
139     if( !p_va->p_display_x11 )
140     {
141         msg_Err( p_external, "Could not connect to X server" );
142         goto error;
143     }
144
145     p_va->p_display = vaGetDisplay( p_va->p_display_x11 );
146     if( !p_va->p_display )
147     {
148         msg_Err( p_external, "Could not get a VAAPI device" );
149         goto error;
150     }
151
152     if( vaInitialize( p_va->p_display, &p_va->i_version_major, &p_va->i_version_minor ) )
153     {
154         msg_Err( p_external, "Failed to initialize the VAAPI device" );
155         goto error;
156     }
157
158     /* Check if the selected profile is supported */
159     i_profiles_nb = vaMaxNumProfiles( p_va->p_display );
160     p_profiles_list = calloc( i_profiles_nb, sizeof( VAProfile ) );
161     if( !p_profiles_list )
162         goto error;
163
164     VAStatus i_status = vaQueryConfigProfiles( p_va->p_display, p_profiles_list, &i_profiles_nb );
165     if ( i_status == VA_STATUS_SUCCESS )
166     {
167         for( int i = 0; i < i_profiles_nb; i++ )
168         {
169             if ( p_profiles_list[i] == i_profile )
170             {
171                 b_supported_profile = true;
172                 break;
173             }
174         }
175     }
176     free( p_profiles_list );
177     if ( !b_supported_profile )
178     {
179         msg_Dbg( p_external, "Codec and profile not supported by the hardware" );
180         goto error;
181     }
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 = VA_INVALID_ID;
198         goto error;
199     }
200
201     p_va->i_surface_count = i_surface_count;
202
203     p_va->b_supports_derive = false;
204
205     if( asprintf( &p_external->description, "VA API version %d.%d",
206                   p_va->i_version_major, p_va->i_version_minor ) < 0 )
207         p_external->description = NULL;
208
209     p_external->sys = p_va;
210     return VLC_SUCCESS;
211
212 error:
213     return VLC_EGENERIC;
214 }
215
216 static void DestroySurfaces( vlc_va_sys_t *p_va )
217 {
218     if( p_va->image.image_id != VA_INVALID_ID )
219     {
220         CopyCleanCache( &p_va->image_cache );
221         vaDestroyImage( p_va->p_display, p_va->image.image_id );
222     }
223     else if(p_va->b_supports_derive)
224     {
225         CopyCleanCache( &p_va->image_cache );
226     }
227
228     if( p_va->i_context_id != VA_INVALID_ID )
229         vaDestroyContext( p_va->p_display, p_va->i_context_id );
230
231     for( int i = 0; i < p_va->i_surface_count && p_va->p_surface; i++ )
232     {
233         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
234
235         if( p_surface->i_id != VA_INVALID_SURFACE )
236             vaDestroySurfaces( p_va->p_display, &p_surface->i_id, 1 );
237     }
238     free( p_va->p_surface );
239
240     /* */
241     p_va->image.image_id = VA_INVALID_ID;
242     p_va->i_context_id = VA_INVALID_ID;
243     p_va->p_surface = NULL;
244     p_va->i_surface_width = 0;
245     p_va->i_surface_height = 0;
246 }
247 static int CreateSurfaces( vlc_va_sys_t *p_va, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
248                            int i_width, int i_height )
249 {
250     assert( i_width > 0 && i_height > 0 );
251
252     /* */
253     p_va->p_surface = calloc( p_va->i_surface_count, sizeof(*p_va->p_surface) );
254     if( !p_va->p_surface )
255         return VLC_EGENERIC;
256     p_va->image.image_id = VA_INVALID_ID;
257     p_va->i_context_id   = VA_INVALID_ID;
258
259     /* Create surfaces */
260     VASurfaceID pi_surface_id[p_va->i_surface_count];
261     if( vaCreateSurfaces( p_va->p_display, i_width, i_height, VA_RT_FORMAT_YUV420,
262                           p_va->i_surface_count, pi_surface_id ) )
263     {
264         for( int i = 0; i < p_va->i_surface_count; i++ )
265             p_va->p_surface[i].i_id = VA_INVALID_SURFACE;
266         goto error;
267     }
268
269     for( int i = 0; i < p_va->i_surface_count; i++ )
270     {
271         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
272
273         p_surface->i_id = pi_surface_id[i];
274         p_surface->i_refcount = 0;
275         p_surface->i_order = 0;
276     }
277
278     /* Create a context */
279     if( vaCreateContext( p_va->p_display, p_va->i_config_id,
280                          i_width, i_height, VA_PROGRESSIVE,
281                          pi_surface_id, p_va->i_surface_count, &p_va->i_context_id ) )
282     {
283         p_va->i_context_id = VA_INVALID_ID;
284         goto error;
285     }
286
287     /* Find and create a supported image chroma */
288     int i_fmt_count = vaMaxNumImageFormats( p_va->p_display );
289     VAImageFormat *p_fmt = calloc( i_fmt_count, sizeof(*p_fmt) );
290     if( !p_fmt )
291         goto error;
292
293     if( vaQueryImageFormats( p_va->p_display, p_fmt, &i_fmt_count ) )
294     {
295         free( p_fmt );
296         goto error;
297     }
298
299     VAImage test_image;
300     if(vaDeriveImage(p_va->p_display, pi_surface_id[0], &test_image) == VA_STATUS_SUCCESS)
301     {
302         p_va->b_supports_derive = true;
303         vaDestroyImage(p_va->p_display, test_image.image_id);
304     }
305
306     vlc_fourcc_t  i_chroma = 0;
307     VAImageFormat fmt;
308     for( int i = 0; i < i_fmt_count; i++ )
309     {
310         if( p_fmt[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) ||
311             p_fmt[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ||
312             p_fmt[i].fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) )
313         {
314             if( vaCreateImage(  p_va->p_display, &p_fmt[i], i_width, i_height, &p_va->image ) )
315             {
316                 p_va->image.image_id = VA_INVALID_ID;
317                 continue;
318             }
319             /* Validate that vaGetImage works with this format */
320             if( vaGetImage( p_va->p_display, pi_surface_id[0],
321                             0, 0, i_width, i_height,
322                             p_va->image.image_id) )
323             {
324                 vaDestroyImage( p_va->p_display, p_va->image.image_id );
325                 p_va->image.image_id = VA_INVALID_ID;
326                 continue;
327             }
328
329             i_chroma = VLC_CODEC_YV12;
330             fmt = p_fmt[i];
331             break;
332         }
333     }
334     free( p_fmt );
335     if( !i_chroma )
336         goto error;
337     *pi_chroma = i_chroma;
338
339     if(p_va->b_supports_derive)
340     {
341         vaDestroyImage( p_va->p_display, p_va->image.image_id );
342         p_va->image.image_id = VA_INVALID_ID;
343     }
344
345     if( unlikely(CopyInitCache( &p_va->image_cache, i_width )) )
346         goto error;
347
348     /* Setup the ffmpeg hardware context */
349     *pp_hw_ctx = &p_va->hw_ctx;
350
351     memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) );
352     p_va->hw_ctx.display    = p_va->p_display;
353     p_va->hw_ctx.config_id  = p_va->i_config_id;
354     p_va->hw_ctx.context_id = p_va->i_context_id;
355
356     /* */
357     p_va->i_surface_chroma = i_chroma;
358     p_va->i_surface_width = i_width;
359     p_va->i_surface_height = i_height;
360     return VLC_SUCCESS;
361
362 error:
363     DestroySurfaces( p_va );
364     return VLC_EGENERIC;
365 }
366
367 static int Setup( vlc_va_t *p_external, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
368                   int i_width, int i_height )
369 {
370     vlc_va_sys_t *p_va = p_external->sys;
371
372     if( p_va->i_surface_width == i_width &&
373         p_va->i_surface_height == i_height )
374     {
375         *pp_hw_ctx = &p_va->hw_ctx;
376         *pi_chroma = p_va->i_surface_chroma;
377         return VLC_SUCCESS;
378     }
379
380     *pp_hw_ctx = NULL;
381     *pi_chroma = 0;
382     if( p_va->i_surface_width || p_va->i_surface_height )
383         DestroySurfaces( p_va );
384
385     if( i_width > 0 && i_height > 0 )
386         return CreateSurfaces( p_va, pp_hw_ctx, pi_chroma, i_width, i_height );
387
388     return VLC_EGENERIC;
389 }
390 static int Extract( vlc_va_t *p_external, picture_t *p_picture, AVFrame *p_ff )
391 {
392     vlc_va_sys_t *p_va = p_external->sys;
393
394     VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
395
396 #if VA_CHECK_VERSION(0,31,0)
397     if( vaSyncSurface( p_va->p_display, i_surface_id ) )
398 #else
399     if( vaSyncSurface( p_va->p_display, p_va->i_context_id, i_surface_id ) )
400 #endif
401         return VLC_EGENERIC;
402
403     if(p_va->b_supports_derive)
404     {
405         if(vaDeriveImage(p_va->p_display, i_surface_id, &(p_va->image)) != VA_STATUS_SUCCESS)
406             return VLC_EGENERIC;
407     }
408     else
409     {
410         if( vaGetImage( p_va->p_display, i_surface_id,
411                         0, 0, p_va->i_surface_width, p_va->i_surface_height,
412                         p_va->image.image_id) )
413             return VLC_EGENERIC;
414     }
415
416     void *p_base;
417     if( vaMapBuffer( p_va->p_display, p_va->image.buf, &p_base ) )
418         return VLC_EGENERIC;
419
420     const uint32_t i_fourcc = p_va->image.format.fourcc;
421     if( i_fourcc == VA_FOURCC('Y','V','1','2') ||
422         i_fourcc == VA_FOURCC('I','4','2','0') )
423     {
424         bool b_swap_uv = i_fourcc == VA_FOURCC('I','4','2','0');
425         uint8_t *pp_plane[3];
426         size_t  pi_pitch[3];
427
428         for( int i = 0; i < 3; i++ )
429         {
430             const int i_src_plane = (b_swap_uv && i != 0) ?  (3 - i) : i;
431             pp_plane[i] = (uint8_t*)p_base + p_va->image.offsets[i_src_plane];
432             pi_pitch[i] = p_va->image.pitches[i_src_plane];
433         }
434         CopyFromYv12( p_picture, pp_plane, pi_pitch,
435                       p_va->i_surface_width,
436                       p_va->i_surface_height,
437                       &p_va->image_cache );
438     }
439     else
440     {
441         assert( i_fourcc == VA_FOURCC('N','V','1','2') );
442         uint8_t *pp_plane[2];
443         size_t  pi_pitch[2];
444
445         for( int i = 0; i < 2; i++ )
446         {
447             pp_plane[i] = (uint8_t*)p_base + p_va->image.offsets[i];
448             pi_pitch[i] = p_va->image.pitches[i];
449         }
450         CopyFromNv12( p_picture, pp_plane, pi_pitch,
451                       p_va->i_surface_width,
452                       p_va->i_surface_height,
453                       &p_va->image_cache );
454     }
455
456     if( vaUnmapBuffer( p_va->p_display, p_va->image.buf ) )
457         return VLC_EGENERIC;
458
459     if(p_va->b_supports_derive)
460     {
461         vaDestroyImage( p_va->p_display, p_va->image.image_id );
462         p_va->image.image_id = VA_INVALID_ID;
463     }
464
465     return VLC_SUCCESS;
466 }
467 static int Get( vlc_va_t *p_external, AVFrame *p_ff )
468 {
469     vlc_va_sys_t *p_va = p_external->sys;
470     int i_old;
471     int i;
472
473     /* Grab an unused surface, in case none are, try the oldest
474      * XXX using the oldest is a workaround in case a problem happens with ffmpeg */
475     for( i = 0, i_old = 0; i < p_va->i_surface_count; i++ )
476     {
477         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
478
479         if( !p_surface->i_refcount )
480             break;
481
482         if( p_surface->i_order < p_va->p_surface[i_old].i_order )
483             i_old = i;
484     }
485     if( i >= p_va->i_surface_count )
486         i = i_old;
487
488     vlc_va_surface_t *p_surface = &p_va->p_surface[i];
489
490     p_surface->i_refcount = 1;
491     p_surface->i_order = p_va->i_surface_order++;
492
493     /* */
494     for( int i = 0; i < 4; i++ )
495     {
496         p_ff->data[i] = NULL;
497         p_ff->linesize[i] = 0;
498
499         if( i == 0 || i == 3 )
500             p_ff->data[i] = (void*)(uintptr_t)p_surface->i_id;/* Yummie */
501     }
502     return VLC_SUCCESS;
503 }
504 static void Release( vlc_va_t *p_external, AVFrame *p_ff )
505 {
506     vlc_va_sys_t *p_va = p_external->sys;
507
508     VASurfaceID i_surface_id = (VASurfaceID)(uintptr_t)p_ff->data[3];
509
510     for( int i = 0; i < p_va->i_surface_count; i++ )
511     {
512         vlc_va_surface_t *p_surface = &p_va->p_surface[i];
513
514         if( p_surface->i_id == i_surface_id )
515             p_surface->i_refcount--;
516     }
517 }
518
519 static void Close( vlc_va_sys_t *p_va )
520 {
521     if( p_va->i_surface_width || p_va->i_surface_height )
522         DestroySurfaces( p_va );
523
524     if( p_va->i_config_id != VA_INVALID_ID )
525         vaDestroyConfig( p_va->p_display, p_va->i_config_id );
526     if( p_va->p_display )
527         vaTerminate( p_va->p_display );
528     if( p_va->p_display_x11 )
529         XCloseDisplay( p_va->p_display_x11 );
530 }
531
532 static void Delete( vlc_va_t *p_external )
533 {
534     vlc_va_sys_t *p_va = p_external->sys;
535     Close( p_va );
536     free( p_external->description );
537     free( p_va );
538 }
539
540 static int Create( vlc_va_t *p_va, int i_codec_id, const es_format_t *fmt )
541 {
542     if( !vlc_xlib_init( VLC_OBJECT(p_va) ) )
543     {
544         msg_Warn( p_va, "Ignoring VA API" );
545         return VLC_EGENERIC;
546     }
547
548     (void) fmt;
549
550     int err = Open( p_va, i_codec_id );
551     if( err )
552         return err;
553
554     /* Only VLD supported */
555     p_va->pix_fmt = PIX_FMT_VAAPI_VLD;
556     p_va->setup = Setup;
557     p_va->get = Get;
558     p_va->release = Release;
559     p_va->extract = Extract;
560     return VLC_SUCCESS;
561 }