]> git.sesse.net Git - vlc/blob - modules/codec/avcodec/vda.c
fd298ebc5419e36ec9723b209dd80edd9e39668f
[vlc] / modules / codec / avcodec / vda.c
1 /*****************************************************************************
2  * vda.c: VDA helpers for the libavcodec decoder
3  *****************************************************************************
4  * Copyright © 2012 VideoLAN
5  *
6  * Authors: Sebastien Zwickert <dilaroga@free.fr>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <assert.h>
28
29 #include <vlc_common.h>
30 #include <vlc_vout.h>
31 #include <vlc_plugin.h>
32
33 #include <libavcodec/avcodec.h>
34
35 #include "avcodec.h"
36 #include "va.h"
37 #include "copy.h"
38
39 #include <libavcodec/vda.h>
40 #include <VideoDecodeAcceleration/VDADecoder.h>
41
42 static int Open( vlc_va_t *, int, const es_format_t * );
43 static void Close( vlc_va_t * );
44
45 static const int  nvda_pix_fmt_list[] = { 0, 1 };
46 static const char *const nvda_pix_fmt_list_text[] =
47   { N_("420YpCbCr8Planar"), N_("422YpCbCr8") };
48
49 vlc_module_begin ()
50     set_description( N_("Video Decode Acceleration Framework (VDA)") )
51     set_capability( "hw decoder", 0 )
52     set_category( CAT_INPUT )
53     set_subcategory( SUBCAT_INPUT_VCODEC )
54     set_callbacks( Open, Close )
55     add_integer ( "avcodec-vda-pix-fmt", 0, VDA_PIX_FMT_TEXT,
56                   VDA_PIX_FMT_LONGTEXT, false)
57         change_integer_list( nvda_pix_fmt_list, nvda_pix_fmt_list_text )
58 vlc_module_end ()
59
60 struct vlc_va_sys_t
61 {
62     struct vda_context  hw_ctx;
63
64     uint8_t             *p_extradata;
65     int                 i_extradata;
66
67     vlc_fourcc_t        i_chroma;
68
69     copy_cache_t        image_cache;
70
71     vlc_object_t        *p_log;
72
73 };
74
75 typedef struct vlc_va_sys_t vlc_va_vda_t;
76
77 static vlc_va_vda_t *vlc_va_vda_Get( vlc_va_t *p_va )
78 {
79     return p_va->sys;
80 }
81
82 /*****************************************************************************
83  * vda_Copy420YpCbCr8Planar: copy y420 CVPixelBuffer to picture_t
84  *****************************************************************************/
85 static void vda_Copy420YpCbCr8Planar( picture_t *p_pic,
86                                       CVPixelBufferRef buffer,
87                                       unsigned i_width,
88                                       unsigned i_height,
89                                       copy_cache_t *cache )
90 {
91     uint8_t *pp_plane[3];
92     size_t  pi_pitch[3];
93
94     if (!buffer)
95         return;
96
97     CVPixelBufferLockBaseAddress( buffer, 0 );
98
99     for( int i = 0; i < 3; i++ )
100     {
101         pp_plane[i] = CVPixelBufferGetBaseAddressOfPlane( buffer, i );
102         pi_pitch[i] = CVPixelBufferGetBytesPerRowOfPlane( buffer, i );
103     }
104
105     CopyFromYv12( p_pic, pp_plane, pi_pitch,
106                   i_width, i_height, cache );
107
108     CVPixelBufferUnlockBaseAddress( buffer, 0 );
109     CVPixelBufferRelease( buffer );
110 }
111
112 /*****************************************************************************
113  * vda_Copy422YpCbCr8: copy 2vuy CVPixelBuffer to picture_t
114  *****************************************************************************/
115 static void vda_Copy422YpCbCr8( picture_t *p_pic,
116                                 CVPixelBufferRef buffer )
117 {
118     int i_plane, i_line, i_dst_stride, i_src_stride;
119     uint8_t *p_dst, *p_src;
120
121     CVPixelBufferLockBaseAddress( buffer, 0 );
122
123     for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
124     {
125         p_dst = p_pic->p[i_plane].p_pixels;
126         p_src = CVPixelBufferGetBaseAddressOfPlane( buffer, i_plane );
127         i_dst_stride  = p_pic->p[i_plane].i_pitch;
128         i_src_stride  = CVPixelBufferGetBytesPerRowOfPlane( buffer, i_plane );
129
130         for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines ; i_line++ )
131         {
132             memcpy( p_dst, p_src, i_src_stride );
133
134             p_src += i_src_stride;
135             p_dst += i_dst_stride;
136         }
137     }
138
139     CVPixelBufferUnlockBaseAddress( buffer, 0 );
140     CVPixelBufferRelease( buffer );
141 }
142
143 static int Setup( vlc_va_t *external, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
144                   int i_width, int i_height )
145 {
146     vlc_va_vda_t *p_va = vlc_va_vda_Get( external );
147
148     if( p_va->hw_ctx.width == i_width
149         && p_va->hw_ctx.height == i_height
150         && p_va->hw_ctx.decoder )
151     {
152         *pp_hw_ctx = &p_va->hw_ctx;
153         *pi_chroma = p_va->i_chroma;
154         return VLC_SUCCESS;
155     }
156
157     if( p_va->hw_ctx.decoder )
158     {
159         ff_vda_destroy_decoder( &p_va->hw_ctx );
160         goto ok;
161     }
162
163     memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) );
164     p_va->hw_ctx.format = 'avc1';
165
166     int i_pix_fmt = var_CreateGetInteger( p_va->p_log, "avcodec-vda-pix-fmt" );
167
168     switch( i_pix_fmt )
169     {
170         case 1 :
171             p_va->hw_ctx.cv_pix_fmt_type = kCVPixelFormatType_422YpCbCr8;
172             p_va->i_chroma = VLC_CODEC_UYVY;
173             msg_Dbg(p_va->p_log, "using pixel format 422YpCbCr8");
174             break;
175         case 0 :
176         default :
177             p_va->hw_ctx.cv_pix_fmt_type = kCVPixelFormatType_420YpCbCr8Planar;
178             p_va->i_chroma = VLC_CODEC_I420;
179             CopyInitCache( &p_va->image_cache, i_width );
180             msg_Dbg(p_va->p_log, "using pixel format 420YpCbCr8Planar");
181     }
182
183 ok:
184     /* Setup the libavcodec hardware context */
185     *pp_hw_ctx = &p_va->hw_ctx;
186     *pi_chroma = p_va->i_chroma;
187
188     p_va->hw_ctx.width = i_width;
189     p_va->hw_ctx.height = i_height;
190
191     /* create the decoder */
192     int status = ff_vda_create_decoder( &p_va->hw_ctx,
193                                         p_va->p_extradata,
194                                         p_va->i_extradata );
195     if( status )
196     {
197         msg_Err( p_va->p_log, "Failed to create decoder: %i", status );
198         return VLC_EGENERIC;
199     }
200     else
201         msg_Dbg( p_va->p_log, "VDA decoder created");
202
203     return VLC_SUCCESS;
204 }
205
206 static int Get( vlc_va_t *external, void **opaque, uint8_t **data )
207 {
208     VLC_UNUSED( external );
209
210     *data = (uint8_t *)1; // dummy
211     (void) opaque;
212     return VLC_SUCCESS;
213 }
214
215 static int Extract( vlc_va_t *external, picture_t *p_picture, AVFrame *p_ff )
216 {
217     vlc_va_vda_t *p_va = vlc_va_vda_Get( external );
218     CVPixelBufferRef cv_buffer = ( CVPixelBufferRef )p_ff->data[3];
219
220     if( !cv_buffer )
221     {
222         msg_Dbg( p_va->p_log, "Frame buffer is empty.");
223         return VLC_EGENERIC;
224     }
225     if (!CVPixelBufferGetDataSize(cv_buffer) > 0)
226     {
227         msg_Dbg( p_va->p_log, "Empty frame buffer");
228         return VLC_EGENERIC;
229     }
230
231     if( p_va->hw_ctx.cv_pix_fmt_type == kCVPixelFormatType_420YpCbCr8Planar )
232     {
233         if( !p_va->image_cache.buffer ) {
234             CVPixelBufferRelease( cv_buffer );
235             return VLC_EGENERIC;
236         }
237
238         vda_Copy420YpCbCr8Planar( p_picture,
239                                   cv_buffer,
240                                   p_va->hw_ctx.width,
241                                   p_va->hw_ctx.height,
242                                   &p_va->image_cache );
243     }
244     else
245         vda_Copy422YpCbCr8( p_picture, cv_buffer );
246
247     return VLC_SUCCESS;
248 }
249
250 static void Release( void *opaque, uint8_t *data )
251 {
252 #if 0
253     CVPixelBufferRef cv_buffer = ( CVPixelBufferRef )p_ff->data[3];
254
255     if ( cv_buffer )
256         CVPixelBufferRelease( cv_buffer );
257 #endif
258     (void) opaque; (void) data;
259 }
260
261 static void Close( vlc_va_t *external )
262 {
263     vlc_va_vda_t *p_va = vlc_va_vda_Get( external );
264
265     msg_Dbg(p_va->p_log, "destroying VDA decoder");
266
267     ff_vda_destroy_decoder( &p_va->hw_ctx ) ;
268
269     if( p_va->hw_ctx.cv_pix_fmt_type == kCVPixelFormatType_420YpCbCr8Planar )
270         CopyCleanCache( &p_va->image_cache );
271
272     free( p_va );
273 }
274
275 static int Open( vlc_va_t *external, int i_codec_id, const es_format_t *fmt )
276 {
277     msg_Dbg( external, "opening VDA module" );
278     if( i_codec_id != AV_CODEC_ID_H264 )
279     {
280         msg_Warn( external, "input codec isn't H264, canceling VDA decoding" );
281         return VLC_EGENERIC;
282     }
283
284     if( fmt->p_extra == NULL || fmt->i_extra < 7 )
285     {
286         msg_Warn( external, "VDA requires extradata." );
287         return VLC_EGENERIC;
288     }
289
290     vlc_va_vda_t *p_va = calloc( 1, sizeof(*p_va) );
291     if( !p_va )
292         return VLC_EGENERIC;
293
294     p_va->p_log = VLC_OBJECT(external);
295     p_va->p_extradata = fmt->p_extra;
296     p_va->i_extradata = fmt->i_extra;
297
298     external->sys = p_va;
299     external->description = (char *)"VDA";
300     external->pix_fmt = PIX_FMT_VDA_VLD;
301     external->setup = Setup;
302     external->get = Get;
303     external->release = Release;
304     external->extract = Extract;
305
306     return VLC_SUCCESS;
307 }