]> git.sesse.net Git - mlt/blob - src/modules/avformat/vdpau.c
Add support for decoding H.264 with VDPAU.
[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         Display *display = (Display*) strtol( mlt_environment( "x11_display" ), NULL, 16 );
54         if ( !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                                 
70                                 mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "X11 Display = %p\n", display );
71                                 if ( VDP_STATUS_OK == create_device( display, screen, &device, &vdp_get_proc_address ) )
72                                 {
73                                         // Allocate the global VDPAU context
74                                         g_vdpau = calloc( 1, sizeof( *g_vdpau ) );
75                                         if ( g_vdpau )
76                                         {
77                                                 g_vdpau->device = device;
78                                                 g_vdpau->decoder = VDP_INVALID_HANDLE;
79                                                 g_vdpau->producer = this;
80                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_GET_ERROR_STRING, (void**) &vdp_get_error_string );
81                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_GET_API_VERSION, (void**) &vdp_get_api_version );
82                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_GET_INFORMATION_STRING, (void**) &vdp_get_information_string );
83                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**) &vdp_surface_create );
84                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, (void**) &vdp_surface_destroy );
85                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**) &vdp_surface_get_bits );
86                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_DECODER_CREATE, (void**) &vdp_decoder_create );
87                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_DECODER_DESTROY, (void**) &vdp_decoder_destroy );
88                                                 vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_DECODER_RENDER, (void**) &vdp_decoder_render );
89                                                 success = 1;
90                                         }
91                                 }
92                         }
93                         if ( !success )
94                         {
95                                 mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU failed to initialize device\n" );
96                                 dlclose( object );
97                         }
98                 }
99         }
100         else
101         {
102                 success = 1;
103                 mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU already initialized\n" );
104         }
105         
106         if ( g_vdpau && g_vdpau->producer != this )
107                 vdpau_decoder_close();
108         
109         return success;
110 }
111
112 static int vdpau_get_buffer( AVCodecContext *codec_context, AVFrame *frame )
113 {
114         int error = 0;
115         producer_avformat this = codec_context->opaque;
116         mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "vdpau_get_buffer\n" );
117         
118         if ( g_vdpau->producer == this && mlt_deque_count( this->vdpau->deque ) )
119         {
120                 struct vdpau_render_state *render = mlt_deque_pop_front( this->vdpau->deque );
121                 
122                 if ( render )
123                 {
124                         frame->data[0] = (uint8_t*) render;
125                         frame->data[1] = (uint8_t*) render;
126                         frame->data[2] = (uint8_t*) render;
127                         frame->linesize[0] = 0;
128                         frame->linesize[1] = 0;
129                         frame->linesize[2] = 0;
130                         frame->type = FF_BUFFER_TYPE_USER;
131                         render->state = FF_VDPAU_STATE_USED_FOR_REFERENCE;
132                         frame->reordered_opaque = codec_context->reordered_opaque;
133                         if ( frame->reference )
134                         {
135                                 frame->age = this->vdpau->ip_age[0];
136                                 this->vdpau->ip_age[0] = this->vdpau->ip_age[1] + 1;
137                                 this->vdpau->ip_age[1] = 1;
138                                 this->vdpau->b_age++;
139                         }
140                         else
141                         {
142                                 frame->age = this->vdpau->b_age;
143                                 this->vdpau->ip_age[0] ++;
144                                 this->vdpau->ip_age[1] ++;
145                                 this->vdpau->b_age = 1;
146                         }
147                 }
148                 else
149                 {
150                         mlt_log_warning( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU surface underrun\n" );
151                         error = -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         return error;
161 }
162
163 static void vdpau_release_buffer( AVCodecContext *codec_context, AVFrame *frame )
164 {
165         producer_avformat this = codec_context->opaque;
166         struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0];
167         mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "vdpau_release_buffer (%x)\n", render->surface );
168         int i;
169
170         render->state &= ~FF_VDPAU_STATE_USED_FOR_REFERENCE;
171         for ( i = 0; i < 4; i++ )
172                 frame->data[i] = NULL;
173         mlt_deque_push_back( this->vdpau->deque, render );
174 }
175
176 static void vdpau_draw_horiz( AVCodecContext *codec_context, const AVFrame *frame, int offset[4], int y, int type, int height )
177 {
178         producer_avformat this = codec_context->opaque;
179         struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0];
180         VdpVideoSurface surface = render->surface;
181         VdpStatus status = vdp_decoder_render( g_vdpau->decoder, surface, (void*) &render->info,
182                 render->bitstream_buffers_used, render->bitstream_buffers );
183         
184         if ( status != VDP_STATUS_OK )
185         {
186                 this->vdpau->is_decoded = 0;
187                 mlt_log_warning( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU failed to decode (%s)\n",
188                         vdp_get_error_string( status ) );
189         }
190         else
191         {
192                 this->vdpau->is_decoded = 1;
193         }
194 }
195
196 static int vdpau_decoder_init( producer_avformat this )
197 {
198         mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "vdpau_decoder_init\n" );
199         int success = 1;
200         
201         this->video_codec->opaque = this;
202         this->video_codec->get_buffer = vdpau_get_buffer;
203         this->video_codec->release_buffer = vdpau_release_buffer;
204         this->video_codec->draw_horiz_band = vdpau_draw_horiz;
205         this->video_codec->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
206         
207         VdpDecoderProfile profile = VDP_DECODER_PROFILE_H264_HIGH;
208         uint32_t max_references = this->video_codec->refs;
209         pthread_mutex_lock( &mlt_sdl_mutex );
210         VdpStatus status = vdp_decoder_create( g_vdpau->device,
211                 profile, this->video_codec->width, this->video_codec->height, max_references, &g_vdpau->decoder );
212         pthread_mutex_unlock( &mlt_sdl_mutex );
213         
214         if ( status == VDP_STATUS_OK )
215         {
216                 if ( !this->vdpau )
217                 {
218                         int i, n = FFMIN( this->video_codec->refs + 1, MAX_VDPAU_SURFACES );
219         
220                         this->vdpau = calloc( 1, sizeof( *this->vdpau ) );
221                         this->vdpau->deque = mlt_deque_init();
222                         for ( i = 0; i < n; i++ )
223                         {
224                                 if ( VDP_STATUS_OK == vdp_surface_create( g_vdpau->device, VDP_CHROMA_TYPE_420,
225                                         this->video_codec->width, this->video_codec->height, &this->vdpau->render_states[i].surface ) )
226                                 {
227                                         mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "successfully created VDPAU surface %x\n",
228                                                 this->vdpau->render_states[i].surface );
229                                         mlt_deque_push_back( this->vdpau->deque, &this->vdpau->render_states[i] );
230                                 }
231                                 else
232                                 {
233                                         mlt_log_info( MLT_PRODUCER_SERVICE(&this->parent), "failed to create VDPAU surface %dx%d\n",
234                                                 this->video_codec->width, this->video_codec->height );
235                                         while ( mlt_deque_count( this->vdpau->deque ) )
236                                         {
237                                                 struct vdpau_render_state *render = mlt_deque_pop_front( this->vdpau->deque );
238                                                 vdp_surface_destroy( render->surface );
239                                         }
240                                         mlt_deque_close( this->vdpau->deque );
241                                         free( this->vdpau );
242                                         this->vdpau = NULL;
243                                         vdp_decoder_destroy( g_vdpau->decoder );
244                                         g_vdpau->decoder = VDP_INVALID_HANDLE;
245                                         success = 0;
246                                         break;
247                                 }
248                         }
249                         this->vdpau->b_age = this->vdpau->ip_age[0] = this->vdpau->ip_age[1] = 256*256*256*64; // magic from Avidemux
250                 }
251                 g_vdpau->producer = this;
252         }
253         else
254         {
255                 success = 0;
256                 g_vdpau->decoder = VDP_INVALID_HANDLE;
257                 mlt_log_error( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU failed to initialize decoder (%s)\n",
258                         vdp_get_error_string( status ) );
259         }
260         
261         return success;
262 }
263
264 static void vdpau_producer_close( producer_avformat this )
265 {
266         if ( this->vdpau )
267         {
268                 mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "vdpau_producer_close\n" );
269                 int i;
270                 for ( i = 0; i < MAX_VDPAU_SURFACES; i++ )
271                 {
272                         if ( this->vdpau->render_states[i].surface != VDP_INVALID_HANDLE )
273                                 vdp_surface_destroy( this->vdpau->render_states[i].surface );
274                         this->vdpau->render_states[i].surface = VDP_INVALID_HANDLE;
275                 }
276                 mlt_deque_close( this->vdpau->deque );
277                 if ( this->vdpau->buffer )
278                         mlt_pool_release( this->vdpau->buffer );
279                 this->vdpau->buffer = NULL;
280                 free( this->vdpau );
281                 this->vdpau = NULL;
282         }
283 }
284
285 static void vdpau_decoder_close( )
286 {
287         mlt_log_debug( NULL, "vdpau_decoder_close (%x)\n", g_vdpau->decoder );
288         if ( g_vdpau && g_vdpau->decoder != VDP_INVALID_HANDLE )
289         {
290                 vdp_decoder_destroy( g_vdpau->decoder );
291                 g_vdpau->decoder = VDP_INVALID_HANDLE;
292                 g_vdpau->producer = NULL;
293         }
294         
295 }
296
297 static void vdpau_close( void *ignored )
298 {
299         mlt_log_debug( NULL, "vdpau_close\n" );
300         if ( g_vdpau )
301         {
302                 vdpau_decoder_close( );
303                 free( g_vdpau );
304                 g_vdpau = NULL;
305         }
306 }