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