]> git.sesse.net Git - mlt/blob - src/modules/avformat/vdpau.c
Fix crash when VDPAU fails init (kdenlive-3079).
[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 <libavcodec/vdpau.h>
22 #include <X11/Xlib.h>
23 #include <dlfcn.h>
24
25 extern pthread_mutex_t mlt_sdl_mutex;
26
27 static VdpDeviceCreateX11      *vdpau_device_create_x11;
28 static VdpDeviceDestroy        *vdp_device_destroy;
29 static VdpGetProcAddress       *vdp_get_proc_address;
30 static VdpGetErrorString       *vdp_get_error_string;
31 static VdpGetApiVersion        *vdp_get_api_version;
32 static VdpGetInformationString *vdp_get_information_string;
33 static VdpVideoSurfaceCreate   *vdp_surface_create;
34 static VdpVideoSurfaceDestroy  *vdp_surface_destroy;
35 static VdpVideoSurfaceGetBitsYCbCr *vdp_surface_get_bits;
36 static VdpDecoderCreate        *vdp_decoder_create;
37 static VdpDecoderDestroy       *vdp_decoder_destroy;
38 static VdpDecoderRender        *vdp_decoder_render;
39
40 // TODO: Shouldn't these be protected by a mutex?
41 static int vdpau_init_done = 0;
42 static int vdpau_supported = 1;
43
44 /** VDPAUD functions
45 */
46
47 static void vdpau_fini( producer_avformat self )
48 {
49         if ( !self->vdpau )
50                 return;
51         mlt_log_debug( NULL, "vdpau_fini (%x)\n", self->vdpau->device );
52         if ( self->vdpau->decoder != VDP_INVALID_HANDLE )
53                 vdp_decoder_destroy( self->vdpau->decoder );
54         if ( self->vdpau->device != VDP_INVALID_HANDLE )
55                 vdp_device_destroy( self->vdpau->device );
56         free( self->vdpau );
57         self->vdpau = NULL;
58 }
59
60 static int vdpau_init( producer_avformat self )
61 {
62         if ( !vdpau_supported )
63                 return 0;
64         mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_init\n" );
65         int success = 0;
66         mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent );
67         Display *display = XOpenDisplay( NULL );
68         
69         if ( !display || mlt_properties_get_int( properties, "novdpau" )
70              || ( getenv( "MLT_NO_VDPAU" ) && strcmp( getenv( "MLT_NO_VDPAU" ), "1" ) == 0 ) )
71                 return success;
72
73         void *object = NULL;
74         if ( !vdpau_init_done )
75         {
76                 int flags = RTLD_NOW;
77                 object = dlopen( "/usr/lib/libvdpau.so", flags );
78 #ifdef ARCH_X86_64
79                 if ( !object )
80                         object = dlopen( "/usr/lib64/libvdpau.so", flags );
81                 if ( !object )
82                         object = dlopen( "/usr/lib/x86_64-linux-gnu/libvdpau.so.1", flags );
83 #elif ARCH_X86
84                 if ( !object )
85                         object = dlopen( "/usr/lib/i386-linux-gnu/libvdpau.so.1", flags );
86 #endif
87                 if ( !object )
88                         object = dlopen( "/usr/local/lib/libvdpau.so", flags );
89                 if ( object )
90                         vdpau_device_create_x11 = dlsym( object, "vdp_device_create_x11" );
91                 else
92                 {
93                         mlt_log( MLT_PRODUCER_SERVICE(self->parent), MLT_LOG_WARNING, "%s: failed to dlopen libvdpau.so\n  (%s)\n", __FUNCTION__, dlerror() );
94                         // Don't try again.
95                         vdpau_supported = 0;
96                         return success;
97                 }
98         }
99                         
100         if ( vdpau_device_create_x11 )
101         {
102                 int screen = mlt_properties_get_int( properties, "x11_screen" );
103
104                 self->vdpau = calloc( 1, sizeof( *self->vdpau ) );
105                 self->vdpau->device = VDP_INVALID_HANDLE;
106                 self->vdpau->decoder = VDP_INVALID_HANDLE;
107                                 
108                 mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "X11 Display = %p\n", display );
109                 if ( VDP_STATUS_OK == vdpau_device_create_x11( display, screen, &self->vdpau->device, &vdp_get_proc_address ) )
110                 {
111                         if ( !vdpau_init_done ) {
112                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_GET_ERROR_STRING, (void**) &vdp_get_error_string );
113                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_GET_API_VERSION, (void**) &vdp_get_api_version );
114                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_GET_INFORMATION_STRING, (void**) &vdp_get_information_string );
115                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**) &vdp_surface_create );
116                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, (void**) &vdp_surface_destroy );
117                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**) &vdp_surface_get_bits );
118                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DECODER_CREATE, (void**) &vdp_decoder_create );
119                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DECODER_DESTROY, (void**) &vdp_decoder_destroy );
120                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DECODER_RENDER, (void**) &vdp_decoder_render );
121                                 vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DEVICE_DESTROY, (void**) &vdp_device_destroy );
122                                 vdpau_init_done = 1;
123                         }
124                         success = 1;
125                 }
126         }
127         
128         if ( !success )
129         {
130                 mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "VDPAU failed to initialize device\n" );
131                 if ( object )
132                         dlclose( object );
133                 if ( self->vdpau )
134                         free( self->vdpau );
135                 self->vdpau = NULL;
136         }
137
138         return success;
139 }
140
141 static enum PixelFormat vdpau_get_format( struct AVCodecContext *s, const enum PixelFormat *fmt )
142 {
143         return PIX_FMT_VDPAU_H264;
144 }
145
146 static int vdpau_get_buffer( AVCodecContext *codec_context, AVFrame *frame )
147 {
148         int error = 0;
149         producer_avformat self = codec_context->opaque;
150         mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_get_buffer\n" );
151         
152         if ( self->vdpau && mlt_deque_count( self->vdpau->deque ) )
153         {
154                 struct vdpau_render_state *render = mlt_deque_pop_front( self->vdpau->deque );
155                 
156                 if ( render )
157                 {
158                         frame->data[0] = (uint8_t*) render;
159                         frame->data[1] = (uint8_t*) render;
160                         frame->data[2] = (uint8_t*) render;
161                         frame->linesize[0] = 0;
162                         frame->linesize[1] = 0;
163                         frame->linesize[2] = 0;
164                         frame->type = FF_BUFFER_TYPE_USER;
165                         render->state = FF_VDPAU_STATE_USED_FOR_REFERENCE;
166                         frame->reordered_opaque = codec_context->reordered_opaque;
167                         if ( frame->reference )
168                         {
169                                 self->vdpau->ip_age[0] = self->vdpau->ip_age[1] + 1;
170                                 self->vdpau->ip_age[1] = 1;
171                                 self->vdpau->b_age++;
172                         }
173                         else
174                         {
175                                 self->vdpau->ip_age[0] ++;
176                                 self->vdpau->ip_age[1] ++;
177                                 self->vdpau->b_age = 1;
178                         }
179                 }
180                 else
181                 {
182                         mlt_log_warning( MLT_PRODUCER_SERVICE(self->parent), "VDPAU surface underrun\n" );
183                         error = -1;
184                 }
185         }
186         else
187         {
188                 mlt_log_warning( MLT_PRODUCER_SERVICE(self->parent), "VDPAU surface underrun\n" );
189                 error = -1;
190         }
191         
192         return error;
193 }
194
195 static void vdpau_release_buffer( AVCodecContext *codec_context, AVFrame *frame )
196 {
197         producer_avformat self = codec_context->opaque;
198         if ( self->vdpau )
199         {
200                 struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0];
201                 mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_release_buffer (%x)\n", render->surface );
202                 int i;
203
204                 render->state &= ~FF_VDPAU_STATE_USED_FOR_REFERENCE;
205                 for ( i = 0; i < 4; i++ )
206                         frame->data[i] = NULL;
207                 mlt_deque_push_back( self->vdpau->deque, render );
208         }
209 }
210
211 static void vdpau_draw_horiz( AVCodecContext *codec_context, const AVFrame *frame, int offset[4], int y, int type, int height )
212 {
213         producer_avformat self = codec_context->opaque;
214         if ( self->vdpau )
215         {
216                 struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0];
217                 VdpVideoSurface surface = render->surface;
218                 VdpStatus status = vdp_decoder_render( self->vdpau->decoder, surface, (void*) &render->info,
219                         render->bitstream_buffers_used, render->bitstream_buffers );
220
221                 if ( status != VDP_STATUS_OK )
222                 {
223                         self->vdpau->is_decoded = 0;
224                         mlt_log_warning( MLT_PRODUCER_SERVICE(self->parent), "VDPAU failed to decode (%s)\n",
225                                 vdp_get_error_string( status ) );
226                 }
227                 else
228                 {
229                         self->vdpau->is_decoded = 1;
230                 }
231         }
232 }
233
234 static int vdpau_decoder_init( producer_avformat self )
235 {
236         mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_decoder_init\n" );
237         int success = 1;
238         
239         self->video_codec->opaque = self;
240         self->video_codec->get_format = vdpau_get_format;
241         self->video_codec->get_buffer = vdpau_get_buffer;
242         self->video_codec->release_buffer = vdpau_release_buffer;
243         self->video_codec->draw_horiz_band = vdpau_draw_horiz;
244         self->video_codec->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
245         self->video_codec->pix_fmt = PIX_FMT_VDPAU_H264;
246         
247         VdpDecoderProfile profile = VDP_DECODER_PROFILE_H264_HIGH;
248         uint32_t max_references = self->video_codec->refs;
249         pthread_mutex_lock( &mlt_sdl_mutex );
250         VdpStatus status = vdp_decoder_create( self->vdpau->device,
251                 profile, self->video_codec->width, self->video_codec->height, max_references, &self->vdpau->decoder );
252         pthread_mutex_unlock( &mlt_sdl_mutex );
253         
254         if ( status == VDP_STATUS_OK )
255         {
256                         int i, n = FFMIN( self->video_codec->refs + 2, MAX_VDPAU_SURFACES );
257
258                         self->vdpau->deque = mlt_deque_init();
259                         for ( i = 0; i < n; i++ )
260                         {
261                                 if ( VDP_STATUS_OK == vdp_surface_create( self->vdpau->device, VDP_CHROMA_TYPE_420,
262                                         self->video_codec->width, self->video_codec->height, &self->vdpau->render_states[i].surface ) )
263                                 {
264                                         mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "successfully created VDPAU surface %x\n",
265                                                 self->vdpau->render_states[i].surface );
266                                         mlt_deque_push_back( self->vdpau->deque, &self->vdpau->render_states[i] );
267                                 }
268                                 else
269                                 {
270                                         mlt_log_info( MLT_PRODUCER_SERVICE(self->parent), "failed to create VDPAU surface %dx%d\n",
271                                                 self->video_codec->width, self->video_codec->height );
272                                         while ( mlt_deque_count( self->vdpau->deque ) )
273                                         {
274                                                 struct vdpau_render_state *render = mlt_deque_pop_front( self->vdpau->deque );
275                                                 vdp_surface_destroy( render->surface );
276                                         }
277                                         mlt_deque_close( self->vdpau->deque );
278                                         success = 0;
279                                         break;
280                                 }
281                         }
282                         if ( self->vdpau )
283                                 self->vdpau->b_age = self->vdpau->ip_age[0] = self->vdpau->ip_age[1] = 256*256*256*64; // magic from Avidemux
284         }
285         else
286         {
287                 success = 0;
288                 self->vdpau->decoder = VDP_INVALID_HANDLE;
289                 mlt_log_error( MLT_PRODUCER_SERVICE(self->parent), "VDPAU failed to initialize decoder (%s)\n",
290                         vdp_get_error_string( status ) );
291         }
292         
293         return success;
294 }
295
296 static void vdpau_producer_close( producer_avformat self )
297 {
298         if ( self->vdpau )
299         {
300                 mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_producer_close\n" );
301                 int i;
302                 for ( i = 0; i < MAX_VDPAU_SURFACES; i++ )
303                 {
304                         if ( self->vdpau->render_states[i].surface != VDP_INVALID_HANDLE )
305                                 vdp_surface_destroy( self->vdpau->render_states[i].surface );
306                         self->vdpau->render_states[i].surface = VDP_INVALID_HANDLE;
307                 }
308
309                 mlt_deque_close( self->vdpau->deque );
310                 if ( self->vdpau->buffer )
311                         mlt_pool_release( self->vdpau->buffer );
312                 self->vdpau->buffer = NULL;
313
314                 vdpau_fini( self );
315         }
316 }