]> git.sesse.net Git - mlt/blob - src/modules/avformat/vdpau.c
Fix segfault in vdpau_init when x11_display not set.
[mlt] / src / modules / avformat / vdpau.c
1 /*
2  * producer_avformat/vdpau.c -- VDPAU functions for the avformat producer
3  * Copyright (C) 2009 Ushodaya Enterprises Limited
4  * Author: Dan Dennedy <dan@dennedy.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <vdpau.h>
22
23 extern pthread_mutex_t mlt_sdl_mutex;
24
25 static VdpGetProcAddress       *vdp_get_proc_address;
26 static VdpGetErrorString       *vdp_get_error_string;
27 static VdpGetApiVersion        *vdp_get_api_version;
28 static VdpGetInformationString *vdp_get_information_string;
29 static VdpVideoSurfaceCreate   *vdp_surface_create;
30 static VdpVideoSurfaceDestroy  *vdp_surface_destroy;
31 static VdpVideoSurfaceGetBitsYCbCr *vdp_surface_get_bits;
32 static VdpDecoderCreate        *vdp_decoder_create;
33 static VdpDecoderDestroy       *vdp_decoder_destroy;
34 static VdpDecoderRender        *vdp_decoder_render;
35
36 struct
37 {
38         VdpDevice device;
39         VdpDecoder decoder;
40         void *producer;
41 } *g_vdpau = NULL;
42
43 /** VDPAUD functions
44 */
45
46 static void vdpau_decoder_close();
47
48 static int vdpau_init( producer_avformat this )
49 {
50         mlt_log_debug( MLT_PRODUCER_SERVICE(this->parent), "vdpau_init\n" );
51         int success = 0;
52         mlt_properties properties = MLT_PRODUCER_PROPERTIES( this->parent );
53         
54         if ( !mlt_environment( "x11_display" ) || mlt_properties_get_int( properties, "novdpau" ) )
55                 return success;
56
57         if ( !g_vdpau )
58         {
59                 int flags = RTLD_NOW;
60                 void *object = dlopen( "/usr/lib/libvdpau.so", flags );
61                 
62                 if ( object )
63                 {
64                         VdpDeviceCreateX11 *create_device = dlsym( object, "vdp_device_create_x11" );
65                         if ( create_device )
66                         {
67                                 int screen = mlt_properties_get_int( properties, "x11_screen" );
68                                 VdpDevice device;
69                                 Display *display = (Display*) strtol( mlt_environment( "x11_display" ), NULL, 16 );
70                                 
71                                 mlt_log_debug( MLT_PRODUCER_SERVICE(this->parent), "X11 Display = %p\n", display );
72                                 if ( VDP_STATUS_OK == create_device( display, screen, &device, &vdp_get_proc_address ) )
73                                 {
74                                         // Allocate the global VDPAU context
75                                         g_vdpau = calloc( 1, sizeof( *g_vdpau ) );
76                                         if ( g_vdpau )
77                                         {
78                                                 g_vdpau->device = device;
79                                                 g_vdpau->decoder = VDP_INVALID_HANDLE;
80                                                 g_vdpau->producer = this;
81                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_GET_ERROR_STRING, (void**) &vdp_get_error_string );
82                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_GET_API_VERSION, (void**) &vdp_get_api_version );
83                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_GET_INFORMATION_STRING, (void**) &vdp_get_information_string );
84                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**) &vdp_surface_create );
85                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, (void**) &vdp_surface_destroy );
86                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**) &vdp_surface_get_bits );
87                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_DECODER_CREATE, (void**) &vdp_decoder_create );
88                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_DECODER_DESTROY, (void**) &vdp_decoder_destroy );
89                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_DECODER_RENDER, (void**) &vdp_decoder_render );
90                                                 success = 1;
91                                         }
92                                 }
93                         }
94                         if ( !success )
95                         {
96                                 mlt_log_debug( MLT_PRODUCER_SERVICE(this->parent), "VDPAU failed to initialize device\n" );
97                                 dlclose( object );
98                         }
99                 }
100         }
101         else
102         {
103                 success = 1;
104                 mlt_log_debug( MLT_PRODUCER_SERVICE(this->parent), "VDPAU already initialized\n" );
105         }
106         
107         if ( g_vdpau && g_vdpau->producer != this )
108                 vdpau_decoder_close();
109         
110         return success;
111 }
112
113 static enum PixelFormat vdpau_get_format( struct AVCodecContext *s, const enum PixelFormat *fmt )
114 {
115         return PIX_FMT_VDPAU_H264;
116 }
117
118 static int vdpau_get_buffer( AVCodecContext *codec_context, AVFrame *frame )
119 {
120         int error = 0;
121         producer_avformat this = codec_context->opaque;
122         mlt_log_debug( MLT_PRODUCER_SERVICE(this->parent), "vdpau_get_buffer\n" );
123         
124         if ( g_vdpau->producer == this && mlt_deque_count( this->vdpau->deque ) )
125         {
126                 struct vdpau_render_state *render = mlt_deque_pop_front( this->vdpau->deque );
127                 
128                 if ( render )
129                 {
130                         frame->data[0] = (uint8_t*) render;
131                         frame->data[1] = (uint8_t*) render;
132                         frame->data[2] = (uint8_t*) render;
133                         frame->linesize[0] = 0;
134                         frame->linesize[1] = 0;
135                         frame->linesize[2] = 0;
136                         frame->type = FF_BUFFER_TYPE_USER;
137                         render->state = FF_VDPAU_STATE_USED_FOR_REFERENCE;
138                         frame->reordered_opaque = codec_context->reordered_opaque;
139                         if ( frame->reference )
140                         {
141                                 frame->age = this->vdpau->ip_age[0];
142                                 this->vdpau->ip_age[0] = this->vdpau->ip_age[1] + 1;
143                                 this->vdpau->ip_age[1] = 1;
144                                 this->vdpau->b_age++;
145                         }
146                         else
147                         {
148                                 frame->age = this->vdpau->b_age;
149                                 this->vdpau->ip_age[0] ++;
150                                 this->vdpau->ip_age[1] ++;
151                                 this->vdpau->b_age = 1;
152                         }
153                 }
154                 else
155                 {
156                         mlt_log_warning( MLT_PRODUCER_SERVICE(this->parent), "VDPAU surface underrun\n" );
157                         error = -1;
158                 }
159         }
160         else
161         {
162                 mlt_log_warning( MLT_PRODUCER_SERVICE(this->parent), "VDPAU surface underrun\n" );
163                 error = -1;
164         }
165         
166         return error;
167 }
168
169 static void vdpau_release_buffer( AVCodecContext *codec_context, AVFrame *frame )
170 {
171         producer_avformat this = codec_context->opaque;
172         struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0];
173         mlt_log_debug( MLT_PRODUCER_SERVICE(this->parent), "vdpau_release_buffer (%x)\n", render->surface );
174         int i;
175
176         render->state &= ~FF_VDPAU_STATE_USED_FOR_REFERENCE;
177         for ( i = 0; i < 4; i++ )
178                 frame->data[i] = NULL;
179         mlt_deque_push_back( this->vdpau->deque, render );
180 }
181
182 static void vdpau_draw_horiz( AVCodecContext *codec_context, const AVFrame *frame, int offset[4], int y, int type, int height )
183 {
184         producer_avformat this = codec_context->opaque;
185         struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0];
186         VdpVideoSurface surface = render->surface;
187         VdpStatus status = vdp_decoder_render( g_vdpau->decoder, surface, (void*) &render->info,
188                 render->bitstream_buffers_used, render->bitstream_buffers );
189         
190         if ( status != VDP_STATUS_OK )
191         {
192                 this->vdpau->is_decoded = 0;
193                 mlt_log_warning( MLT_PRODUCER_SERVICE(this->parent), "VDPAU failed to decode (%s)\n",
194                         vdp_get_error_string( status ) );
195         }
196         else
197         {
198                 this->vdpau->is_decoded = 1;
199         }
200 }
201
202 static int vdpau_decoder_init( producer_avformat this )
203 {
204         mlt_log_debug( MLT_PRODUCER_SERVICE(this->parent), "vdpau_decoder_init\n" );
205         int success = 1;
206         
207         this->video_codec->opaque = this;
208         this->video_codec->get_format = vdpau_get_format;
209         this->video_codec->get_buffer = vdpau_get_buffer;
210         this->video_codec->release_buffer = vdpau_release_buffer;
211         this->video_codec->draw_horiz_band = vdpau_draw_horiz;
212         this->video_codec->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
213         this->video_codec->pix_fmt = PIX_FMT_VDPAU_H264;
214         
215         VdpDecoderProfile profile = VDP_DECODER_PROFILE_H264_HIGH;
216         uint32_t max_references = this->video_codec->refs;
217         pthread_mutex_lock( &mlt_sdl_mutex );
218         VdpStatus status = vdp_decoder_create( g_vdpau->device,
219                 profile, this->video_codec->width, this->video_codec->height, max_references, &g_vdpau->decoder );
220         pthread_mutex_unlock( &mlt_sdl_mutex );
221         
222         if ( status == VDP_STATUS_OK )
223         {
224                 if ( !this->vdpau )
225                 {
226                         int i, n = FFMIN( this->video_codec->refs + 1, MAX_VDPAU_SURFACES );
227         
228                         this->vdpau = calloc( 1, sizeof( *this->vdpau ) );
229                         this->vdpau->deque = mlt_deque_init();
230                         for ( i = 0; i < n; i++ )
231                         {
232                                 if ( VDP_STATUS_OK == vdp_surface_create( g_vdpau->device, VDP_CHROMA_TYPE_420,
233                                         this->video_codec->width, this->video_codec->height, &this->vdpau->render_states[i].surface ) )
234                                 {
235                                         mlt_log_debug( MLT_PRODUCER_SERVICE(this->parent), "successfully created VDPAU surface %x\n",
236                                                 this->vdpau->render_states[i].surface );
237                                         mlt_deque_push_back( this->vdpau->deque, &this->vdpau->render_states[i] );
238                                 }
239                                 else
240                                 {
241                                         mlt_log_info( MLT_PRODUCER_SERVICE(this->parent), "failed to create VDPAU surface %dx%d\n",
242                                                 this->video_codec->width, this->video_codec->height );
243                                         while ( mlt_deque_count( this->vdpau->deque ) )
244                                         {
245                                                 struct vdpau_render_state *render = mlt_deque_pop_front( this->vdpau->deque );
246                                                 vdp_surface_destroy( render->surface );
247                                         }
248                                         mlt_deque_close( this->vdpau->deque );
249                                         free( this->vdpau );
250                                         this->vdpau = NULL;
251                                         vdp_decoder_destroy( g_vdpau->decoder );
252                                         g_vdpau->decoder = VDP_INVALID_HANDLE;
253                                         success = 0;
254                                         break;
255                                 }
256                         }
257                         this->vdpau->b_age = this->vdpau->ip_age[0] = this->vdpau->ip_age[1] = 256*256*256*64; // magic from Avidemux
258                 }
259                 g_vdpau->producer = this;
260         }
261         else
262         {
263                 success = 0;
264                 g_vdpau->decoder = VDP_INVALID_HANDLE;
265                 mlt_log_error( MLT_PRODUCER_SERVICE(this->parent), "VDPAU failed to initialize decoder (%s)\n",
266                         vdp_get_error_string( status ) );
267         }
268         
269         return success;
270 }
271
272 static void vdpau_producer_close( producer_avformat this )
273 {
274         if ( this->vdpau )
275         {
276                 mlt_log_debug( MLT_PRODUCER_SERVICE(this->parent), "vdpau_producer_close\n" );
277                 int i;
278                 for ( i = 0; i < MAX_VDPAU_SURFACES; i++ )
279                 {
280                         if ( this->vdpau->render_states[i].surface != VDP_INVALID_HANDLE )
281                                 vdp_surface_destroy( this->vdpau->render_states[i].surface );
282                         this->vdpau->render_states[i].surface = VDP_INVALID_HANDLE;
283                 }
284                 mlt_deque_close( this->vdpau->deque );
285                 if ( this->vdpau->buffer )
286                         mlt_pool_release( this->vdpau->buffer );
287                 this->vdpau->buffer = NULL;
288                 free( this->vdpau );
289                 this->vdpau = NULL;
290         }
291 }
292
293 static void vdpau_decoder_close( )
294 {
295         mlt_log_debug( NULL, "vdpau_decoder_close (%x)\n", g_vdpau->decoder );
296         if ( g_vdpau && g_vdpau->decoder != VDP_INVALID_HANDLE )
297         {
298                 vdp_decoder_destroy( g_vdpau->decoder );
299                 g_vdpau->decoder = VDP_INVALID_HANDLE;
300                 g_vdpau->producer = NULL;
301         }
302         
303 }
304
305 static void vdpau_close( void *ignored )
306 {
307         mlt_log_debug( NULL, "vdpau_close\n" );
308         if ( g_vdpau )
309         {
310                 vdpau_decoder_close( );
311                 free( g_vdpau );
312                 g_vdpau = NULL;
313         }
314 }