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