]> git.sesse.net Git - vlc/blob - modules/codec/avcodec/vda.c
avcodec: make vlc_va_t a VLC object
[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 <vlc_common.h>
28 #include <vlc_vout.h>
29 #include <assert.h>
30
31 #include <libavcodec/avcodec.h>
32
33 #include "avcodec.h"
34 #include "va.h"
35 #include "copy.h"
36
37 #include <libavcodec/vda.h>
38 #include <VideoDecodeAcceleration/VDADecoder.h>
39
40 struct vlc_va_sys_t
41 {
42     struct vda_context  hw_ctx;
43
44     const uint8_t       *p_extradata;
45     int                 i_extradata;
46
47     vlc_fourcc_t        i_chroma;
48
49     copy_cache_t        image_cache;
50
51     vlc_object_t        *p_log;
52
53 };
54
55 typedef struct vlc_va_sys_t vlc_va_vda_t;
56
57 static vlc_va_vda_t *vlc_va_vda_Get( vlc_va_t *p_va )
58 {
59     return p_va->sys;
60 }
61
62 /*****************************************************************************
63  * vda_Copy420YpCbCr8Planar: copy y420 CVPixelBuffer to picture_t
64  *****************************************************************************/
65 static void vda_Copy420YpCbCr8Planar( picture_t *p_pic,
66                                       CVPixelBufferRef buffer,
67                                       unsigned i_width,
68                                       unsigned i_height,
69                                       copy_cache_t *cache )
70 {
71     uint8_t *pp_plane[3];
72     size_t  pi_pitch[3];
73
74     CVPixelBufferLockBaseAddress( buffer, 0 );
75
76     for( int i = 0; i < 3; i++ )
77     {
78         pp_plane[i] = CVPixelBufferGetBaseAddressOfPlane( buffer, i );
79         pi_pitch[i] = CVPixelBufferGetBytesPerRowOfPlane( buffer, i );
80     }
81
82     CopyFromYv12( p_pic, pp_plane, pi_pitch,
83                   i_width, i_height, cache );
84
85     CVPixelBufferUnlockBaseAddress( buffer, 0 );
86 }
87
88 /*****************************************************************************
89  * vda_Copy422YpCbCr8: copy 2vuy CVPixelBuffer to picture_t
90  *****************************************************************************/
91 static void vda_Copy422YpCbCr8( picture_t *p_pic,
92                                 CVPixelBufferRef buffer )
93 {
94     int i_plane, i_line, i_dst_stride, i_src_stride;
95     uint8_t *p_dst, *p_src;
96
97     CVPixelBufferLockBaseAddress( buffer, 0 );
98
99     for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
100     {
101         p_dst = p_pic->p[i_plane].p_pixels;
102         p_src = CVPixelBufferGetBaseAddressOfPlane( buffer, i_plane );
103         i_dst_stride  = p_pic->p[i_plane].i_pitch;
104         i_src_stride  = CVPixelBufferGetBytesPerRowOfPlane( buffer, i_plane );
105
106         for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines ; i_line++ )
107         {
108             memcpy( p_dst, p_src, i_src_stride );
109
110             p_src += i_src_stride;
111             p_dst += i_dst_stride;
112         }
113     }
114
115     CVPixelBufferUnlockBaseAddress( buffer, 0 );
116 }
117
118 static int Setup( vlc_va_t *p_external, void **pp_hw_ctx, vlc_fourcc_t *pi_chroma,
119                   int i_width, int i_height )
120 {
121
122     vlc_va_vda_t *p_va = vlc_va_vda_Get( p_external );
123
124     if( p_va->hw_ctx.width == i_width
125         && p_va->hw_ctx.height == i_height
126         && p_va->hw_ctx.decoder )
127     {
128         *pp_hw_ctx = &p_va->hw_ctx;
129         *pi_chroma = p_va->i_chroma;
130         return VLC_SUCCESS;
131     }
132
133     if( p_va->hw_ctx.decoder )
134     {
135         ff_vda_destroy_decoder( &p_va->hw_ctx );
136         goto ok;
137     }
138
139     memset( &p_va->hw_ctx, 0, sizeof(p_va->hw_ctx) );
140     p_va->hw_ctx.width = i_width;
141     p_va->hw_ctx.height = i_height;
142     p_va->hw_ctx.format = 'avc1';
143     p_va->hw_ctx.use_sync_decoding = 1;
144
145     int i_pix_fmt = var_CreateGetInteger( p_va->p_log, "avcodec-vda-pix-fmt" );
146
147     switch( i_pix_fmt )
148     {
149         case 1 :
150             p_va->hw_ctx.cv_pix_fmt_type = kCVPixelFormatType_422YpCbCr8;
151             p_va->i_chroma = VLC_CODEC_UYVY;
152             break;
153         case 0 :
154         default :
155             p_va->hw_ctx.cv_pix_fmt_type = kCVPixelFormatType_420YpCbCr8Planar;
156             p_va->i_chroma = VLC_CODEC_I420;
157             CopyInitCache( &p_va->image_cache, i_width );
158     }
159
160 ok:
161     /* Setup the libavcodec hardware context */
162     *pp_hw_ctx = &p_va->hw_ctx;
163     *pi_chroma = p_va->i_chroma;
164
165     /* create the decoder */
166     int status = ff_vda_create_decoder( &p_va->hw_ctx,
167                                         p_va->p_extradata,
168                                         p_va->i_extradata );
169     if( status )
170     {
171         msg_Err( p_va->p_log, "Failed to create the decoder : %i", status );
172         return VLC_EGENERIC;
173     }
174
175     return VLC_SUCCESS;
176 }
177
178 static int Get( vlc_va_t *p_external, AVFrame *p_ff )
179 {
180     VLC_UNUSED( p_external );
181
182     /* */
183     for( int i = 0; i < 4; i++ )
184     {
185         p_ff->data[i] = NULL;
186         p_ff->linesize[i] = 0;
187
188         if( i == 0 || i == 3 )
189         p_ff->data[i] = 1; // dummy
190     }
191
192     return VLC_SUCCESS;
193 }
194
195 static int Extract( vlc_va_t *p_external, picture_t *p_picture, AVFrame *p_ff )
196 {
197     vlc_va_vda_t *p_va = vlc_va_vda_Get( p_external );
198     CVPixelBufferRef cv_buffer = ( CVPixelBufferRef )p_ff->data[3];
199
200     if( !cv_buffer )
201     {
202         msg_Dbg( p_va->p_log, "Frame buffer is empty.");
203         return VLC_EGENERIC;
204     }
205
206     if( p_va->hw_ctx.cv_pix_fmt_type == kCVPixelFormatType_420YpCbCr8Planar )
207     {
208         if( !p_va->image_cache.buffer )
209             return VLC_EGENERIC;
210
211         vda_Copy420YpCbCr8Planar( p_picture,
212                                   cv_buffer,
213                                   p_va->hw_ctx.width,
214                                   p_va->hw_ctx.height,
215                                   &p_va->image_cache );
216     }
217     else
218         vda_Copy422YpCbCr8( p_picture, cv_buffer );
219
220     return VLC_SUCCESS;
221 }
222
223 static void Release( vlc_va_t *p_external, AVFrame *p_ff )
224 {
225     VLC_UNUSED( p_external );
226     CVPixelBufferRef cv_buffer = ( CVPixelBufferRef )p_ff->data[3];
227
228     if ( cv_buffer )
229         CFRelease( cv_buffer );
230 }
231
232 static void Close( vlc_va_t *p_external )
233 {
234     vlc_va_vda_t *p_va = vlc_va_vda_Get( p_external );
235
236     ff_vda_destroy_decoder( &p_va->hw_ctx ) ;
237
238     if( p_va->hw_ctx.cv_pix_fmt_type == kCVPixelFormatType_420YpCbCr8Planar )
239         CopyCleanCache( &p_va->image_cache );
240
241     free( p_va );
242 }
243
244 int vlc_va_New( vlc_va_t *external, int pixfmt, int i_codec_id,
245                 const es_format_t *fmt )
246 {
247     if( pixfmt != PIX_FMT_VDA_VLD || i_codec_id != CODEC_ID_H264 )
248         return NULL;
249
250     if( fmt->p_extra == NULL || fmt->i_extra < 7 )
251     {
252         msg_Warn( p_log, "VDA requires extradata." );
253         return NULL;
254     }
255
256     vlc_va_vda_t *p_va = calloc( 1, sizeof(*p_va) );
257     if( !p_va )
258         return NULL;
259
260     p_va->p_log = VLC_OBJECT(external);
261     p_va->p_extradata = fmt->p_extra;
262     p_va->i_extradata = fmt->i_extra;
263
264     external->sys = p_va;
265     external->description = (char *)"VDA";
266     external->setup = Setup;
267     external->get = Get;
268     external->release = Release;
269     external->extract = Extract;
270     external->close = Close;
271
272     return VLC_SUCCESS;
273 }