]> git.sesse.net Git - mlt/commitdiff
Add support for decoding H.264 with VDPAU.
authorDan Dennedy <dan@dennedy.org>
Sun, 29 Nov 2009 01:13:01 +0000 (17:13 -0800)
committerDan Dennedy <dan@dennedy.org>
Fri, 11 Dec 2009 04:37:56 +0000 (20:37 -0800)
This applies to all H.264 at the moment unless novdpau=1 is set on the
producer. Also, this can only handle up to about 10 - 15 clips using
VDPAU in the project at the moment until the avformat producer is
changed to use mlt_cache.

src/modules/avformat/Makefile
src/modules/avformat/configure
src/modules/avformat/producer_avformat.c
src/modules/avformat/vdpau.c [new file with mode: 0644]

index 1e275c115a33c97b7158041ac62b4e060364a02c..a0205adcf948239e1d3aa46bfb523f0c781f1409 100644 (file)
@@ -28,6 +28,11 @@ endif
 CFLAGS += -DFILTERS
 endif
 
+ifdef VDPAU
+CFLAGS += -DVDPAU
+LDFLAGS += -ldl
+endif
+
 ifdef CODECS
 OBJS += producer_avformat.o \
            consumer_avformat.o
index 80152f58961f081396d9f7199e821f8f5dcebb1c..356389a47cd3e97393a7c8664323692d48483893 100755 (executable)
@@ -62,6 +62,7 @@ else
        export swscale=
        export codecs=true
        export filters=true
+       export vdpau=
 
        for i in "$@"
        do
@@ -115,6 +116,8 @@ else
                echo "LOCAL_FFMPEG=1" >> config.mak
                echo "LDFLAGS+=-Wl,-Bsymbolic" >> config.mak
                extra_libs="$extra_libs -lm -lz -lbz2"
+               echo -e "#include <libavcodec/vdpau.h>\n int main(){ VdpBitstreamBuffer test; test.struct_version; return 0;}" | gcc -I"`pwd`/ffmpeg" $CFLAGS -c -x c -  >/dev/null 2>&1
+               [ "$?" = "0" ] && echo "VDPAU=1" >> config.mak
        elif [ "$static_ffmpeg" != "" ]
        then 
                if [ -d "$static_ffmpeg" ]
@@ -131,6 +134,8 @@ else
                        fi
                        echo "LDFLAGS+=-Wl,-Bsymbolic" >> config.mak
                        extra_libs="$extra_libs -lm -lz -lbz2"
+                       echo -e "#include <libavcodec/vdpau.h>\n int main(){ VdpBitstreamBuffer test; test.struct_version; return 0;}" | gcc -I"$static_ffmpeg" $CFLAGS -c -x c -  >/dev/null 2>&1
+                       [ "$?" = "0" ] && echo "VDPAU=1" >> config.mak
                else
                        echo "avformat: Invalid path specified: $static_ffmpeg"
                        touch ../disable-avformat
@@ -154,6 +159,8 @@ else
                                echo "CFLAGS+=-I$shared_ffmpeg/include/libswscale" >> config.mak
                        echo "SWSCALE=1" >> config.mak
                fi
+               echo -e "#include <libavcodec/vdpau.h>\n int main(){ VdpBitstreamBuffer test; test.struct_version; return 0;}" | gcc -I"$(pkg-config --cflags libavformat)" -I"$shared_ffmpeg/include" $CFLAGS -c -x c -  >/dev/null 2>&1
+               [ "$?" = "0" ] && echo "VDPAU=1" >> config.mak
        else
                echo "avformat: No build environment found. "
                echo "          Try configuring mlt with --avformat-svn."
index 78b3906029d8f36ae446945890b4fdaf8cc8c447..7b5ddbb8dfaa28a17570860f53d89b06f1edfeaa 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * producer_avformat.c -- avformat producer
- * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
  * Author: Charles Yates <charles.yates@pandora.be>
+ * Author: Dan Dennedy <dan@dennedy.org>
  * Much code borrowed from ffmpeg.c: Copyright (c) 2000-2003 Fabrice Bellard
  *
  * This library is free software; you can redistribute it and/or
@@ -24,6 +25,8 @@
 #include <framework/mlt_frame.h>
 #include <framework/mlt_profile.h>
 #include <framework/mlt_log.h>
+#include <framework/mlt_deque.h>
+#include <framework/mlt_factory.h>
 
 // ffmpeg Header files
 #include <avformat.h>
 #if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0))
 #  include "audioconvert.h"
 #endif
+#ifdef VDPAU
+#include <vdpau.h>
+#endif
 
 // System header files
 #include <stdlib.h>
 #include <string.h>
 #include <pthread.h>
 #include <limits.h>
+#include <dlfcn.h>
 
 #if LIBAVUTIL_VERSION_INT < (50<<16)
 #define PIX_FMT_RGB32 PIX_FMT_RGBA32
@@ -50,6 +57,7 @@
 #define POSITION_INVALID (-1)
 
 #define MAX_AUDIO_STREAMS (8)
+#define MAX_VDPAU_SURFACES (10)
 
 void avformat_lock( );
 void avformat_unlock( );
@@ -86,6 +94,20 @@ struct producer_avformat_s
        int max_frequency;
        unsigned int invalid_pts_counter;
        double resample_factor;
+#ifdef VDPAU
+       struct
+       {
+               // from FFmpeg
+               struct vdpau_render_state render_states[MAX_VDPAU_SURFACES];
+               
+               // internal
+               mlt_deque deque;
+               int b_age;
+               int ip_age[2];
+               int is_decoded;
+               uint8_t *buffer;
+       } *vdpau;
+#endif
 };
 typedef struct producer_avformat_s *producer_avformat;
 
@@ -95,6 +117,10 @@ static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index
 static void producer_format_close( void *context );
 static void producer_close( mlt_producer parent );
 
+#ifdef VDPAU
+#include "vdpau.c"
+#endif
+
 /** Constructor for libavformat.
 */
 
@@ -968,6 +994,20 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form
        {
                // Duplicate it
                if ( allocate_buffer( frame_properties, codec_context, buffer, format, width, height ) )
+#ifdef VDPAU
+                       if ( this->vdpau && this->vdpau->buffer )
+                       {
+                               AVPicture picture;
+                               picture.data[0] = this->vdpau->buffer;
+                               picture.data[2] = this->vdpau->buffer + codec_context->width * codec_context->height;
+                               picture.data[1] = this->vdpau->buffer + codec_context->width * codec_context->height * 5 / 4;
+                               picture.linesize[0] = codec_context->width;
+                               picture.linesize[1] = codec_context->width / 2;
+                               picture.linesize[2] = codec_context->width / 2;
+                               convert_image( (AVFrame*) &picture, *buffer, PIX_FMT_YUV420P, format, *width, *height );
+                       }
+                       else
+#endif
                        convert_image( this->av_frame, *buffer, codec_context->pix_fmt, format, *width, *height );
                else
                        mlt_frame_get_image( frame, buffer, format, width, height, writable );
@@ -1017,7 +1057,7 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form
                                        {
                                                this->invalid_pts_counter = 0;
                                        }
-                                       mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pkt.pts %llu req_pos %d cur_pos %d pkt_pos %d",
+                                       mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pkt.pts %llu req_pos %d cur_pos %d pkt_pos %d\n",
                                                pkt.pts, req_position, this->current_position, int_position );
                                }
                                else
@@ -1035,7 +1075,7 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form
                                        {
                                                int_position = req_position;
                                        }
-                                       mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pkt.dts %llu req_pos %d cur_pos %d pkt_pos %d",
+                                       mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pkt.dts %llu req_pos %d cur_pos %d pkt_pos %d\n",
                                                pkt.dts, req_position, this->current_position, int_position );
                                        // Make a dumb assumption on streams that contain wild timestamps
                                        if ( abs( req_position - int_position ) > 999 )
@@ -1049,6 +1089,18 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form
                                // Decode the image
                                if ( must_decode || int_position >= req_position )
                                {
+#ifdef VDPAU
+                                       if ( g_vdpau && this->vdpau )
+                                       {
+                                               if ( g_vdpau->producer != this )
+                                               {
+                                                       vdpau_decoder_close();
+                                                       vdpau_decoder_init( this );
+                                               }
+                                               if ( this->vdpau )
+                                                       this->vdpau->is_decoded = 0;
+                                       }
+#endif
                                        codec_context->reordered_opaque = pkt.pts;
                                        if ( int_position >= req_position )
                                                codec_context->skip_loop_filter = AVDISCARD_NONE;
@@ -1111,6 +1163,45 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form
                        {
                                if ( allocate_buffer( frame_properties, codec_context, buffer, format, width, height ) )
                                {
+#ifdef VDPAU
+                                       if ( this->vdpau )
+                                       {
+                                               if ( this->vdpau->is_decoded )
+                                               {
+                                                       struct vdpau_render_state *render = (struct vdpau_render_state*) this->av_frame->data[0];
+                                                       void *planes[3];
+                                                       uint32_t pitches[3];
+                                                       VdpYCbCrFormat dest_format = VDP_YCBCR_FORMAT_YV12;
+                                                       AVPicture picture;
+                                                       
+                                                       if ( !this->vdpau->buffer )
+                                                               this->vdpau->buffer = mlt_pool_alloc( codec_context->width * codec_context->height * 3 / 2 );
+                                                       picture.data[0] = planes[0] = this->vdpau->buffer;
+                                                       picture.data[2] = planes[1] = this->vdpau->buffer + codec_context->width * codec_context->height;
+                                                       picture.data[1] = planes[2] = this->vdpau->buffer + codec_context->width * codec_context->height * 5 / 4;
+                                                       picture.linesize[0] = pitches[0] = codec_context->width;
+                                                       picture.linesize[1] = pitches[1] = codec_context->width / 2;
+                                                       picture.linesize[2] = pitches[2] = codec_context->width / 2;
+
+                                                       VdpStatus status = vdp_surface_get_bits( render->surface, dest_format, planes, pitches );
+                                                       if ( status == VDP_STATUS_OK )
+                                                       {
+                                                               convert_image( (AVFrame*) &picture, *buffer, PIX_FMT_YUV420P, format, *width, *height );
+                                                       }
+                                                       else
+                                                       {
+                                                               mlt_log_error( MLT_PRODUCER_SERVICE(producer), "VDPAU Error: %s\n", vdp_get_error_string( status ) );
+                                                               this->vdpau->is_decoded = 0;
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       mlt_log_error( MLT_PRODUCER_SERVICE(producer), "VDPAU error in VdpDecoderRender\n" );
+                                                       got_picture = 0;
+                                               }
+                                       }
+                                       else
+#endif
                                        convert_image( this->av_frame, *buffer, codec_context->pix_fmt, format, *width, *height );
                                        if ( !mlt_properties_get( properties, "force_progressive" ) )
                                                mlt_properties_set_int( frame_properties, "progressive", !this->av_frame->interlaced_frame );
@@ -1182,6 +1273,22 @@ static int video_codec_init( producer_avformat this, int index, mlt_properties p
 
                // Find the codec
                AVCodec *codec = avcodec_find_decoder( codec_context->codec_id );
+#ifdef VDPAU
+               if ( codec_context->codec_id == CODEC_ID_H264 )
+               {
+                       if ( ( codec = avcodec_find_decoder_by_name( "h264_vdpau" ) ) )
+                       {
+                               if ( vdpau_init( this ) )
+                               {
+                                       this->video_codec = codec_context;
+                                       if ( !vdpau_decoder_init( this ) )
+                                               vdpau_decoder_close();
+                               }
+                       }
+                       if ( !this->vdpau )
+                               codec = avcodec_find_decoder( codec_context->codec_id );
+               }
+#endif
 
                // Initialise multi-threading
                int thread_count = mlt_properties_get_int( properties, "threads" );
@@ -1198,7 +1305,6 @@ static int video_codec_init( producer_avformat this, int index, mlt_properties p
                if ( codec && avcodec_open( codec_context, codec ) >= 0 )
                {
                        // Now store the codec with its destructor
-                       producer_codec_close( this->video_codec );
                        this->video_codec = codec_context;
                }
                else
@@ -1835,6 +1941,9 @@ static void producer_close( mlt_producer parent )
        producer_format_close( this->dummy_context );
        producer_format_close( this->audio_format );
        producer_format_close( this->video_format );
+#ifdef VDPAU
+       vdpau_producer_close( this );
+#endif
 
        // Close the parent
        parent->close = NULL;
diff --git a/src/modules/avformat/vdpau.c b/src/modules/avformat/vdpau.c
new file mode 100644 (file)
index 0000000..42a2f7d
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * producer_avformat/vdpau.c -- VDPAU functions for the avformat producer
+ * Copyright (C) 2009 Ushodaya Enterprises Limited
+ * Author: Dan Dennedy <dan@dennedy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <vdpau.h>
+
+extern pthread_mutex_t mlt_sdl_mutex;
+
+static VdpGetProcAddress       *vdp_get_proc_address;
+static VdpGetErrorString       *vdp_get_error_string;
+static VdpGetApiVersion        *vdp_get_api_version;
+static VdpGetInformationString *vdp_get_information_string;
+static VdpVideoSurfaceCreate   *vdp_surface_create;
+static VdpVideoSurfaceDestroy  *vdp_surface_destroy;
+static VdpVideoSurfaceGetBitsYCbCr *vdp_surface_get_bits;
+static VdpDecoderCreate        *vdp_decoder_create;
+static VdpDecoderDestroy       *vdp_decoder_destroy;
+static VdpDecoderRender        *vdp_decoder_render;
+
+struct
+{
+       VdpDevice device;
+       VdpDecoder decoder;
+       void *producer;
+} *g_vdpau = NULL;
+
+/** VDPAUD functions
+*/
+
+static void vdpau_decoder_close();
+
+static int vdpau_init( producer_avformat this )
+{
+       mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "vdpau_init\n" );
+       int success = 0;
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
+       Display *display = (Display*) strtol( mlt_environment( "x11_display" ), NULL, 16 );
+       if ( !display || mlt_properties_get_int( properties, "novdpau" ) )
+               return success;
+
+       if ( !g_vdpau )
+       {
+               int flags = RTLD_NOW;
+               void *object = dlopen( "/usr/lib/libvdpau.so", flags );
+               
+               if ( object )
+               {
+                       VdpDeviceCreateX11 *create_device = dlsym( object, "vdp_device_create_x11" );
+                       if ( create_device )
+                       {
+                               int screen = mlt_properties_get_int( properties, "x11_screen" );
+                               VdpDevice device;
+                               
+                               mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "X11 Display = %p\n", display );
+                               if ( VDP_STATUS_OK == create_device( display, screen, &device, &vdp_get_proc_address ) )
+                               {
+                                       // Allocate the global VDPAU context
+                                       g_vdpau = calloc( 1, sizeof( *g_vdpau ) );
+                                       if ( g_vdpau )
+                                       {
+                                               g_vdpau->device = device;
+                                               g_vdpau->decoder = VDP_INVALID_HANDLE;
+                                               g_vdpau->producer = this;
+                                               vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_GET_ERROR_STRING, (void**) &vdp_get_error_string );
+                                               vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_GET_API_VERSION, (void**) &vdp_get_api_version );
+                                               vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_GET_INFORMATION_STRING, (void**) &vdp_get_information_string );
+                                               vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**) &vdp_surface_create );
+                                               vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, (void**) &vdp_surface_destroy );
+                                               vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**) &vdp_surface_get_bits );
+                                               vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_DECODER_CREATE, (void**) &vdp_decoder_create );
+                                               vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_DECODER_DESTROY, (void**) &vdp_decoder_destroy );
+                                               vdp_get_proc_address( g_vdpau->device, VDP_FUNC_ID_DECODER_RENDER, (void**) &vdp_decoder_render );
+                                               success = 1;
+                                       }
+                               }
+                       }
+                       if ( !success )
+                       {
+                               mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU failed to initialize device\n" );
+                               dlclose( object );
+                       }
+               }
+       }
+       else
+       {
+               success = 1;
+               mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU already initialized\n" );
+       }
+       
+       if ( g_vdpau && g_vdpau->producer != this )
+               vdpau_decoder_close();
+       
+       return success;
+}
+
+static int vdpau_get_buffer( AVCodecContext *codec_context, AVFrame *frame )
+{
+       int error = 0;
+       producer_avformat this = codec_context->opaque;
+       mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "vdpau_get_buffer\n" );
+       
+       if ( g_vdpau->producer == this && mlt_deque_count( this->vdpau->deque ) )
+       {
+               struct vdpau_render_state *render = mlt_deque_pop_front( this->vdpau->deque );
+               
+               if ( render )
+               {
+                       frame->data[0] = (uint8_t*) render;
+                       frame->data[1] = (uint8_t*) render;
+                       frame->data[2] = (uint8_t*) render;
+                       frame->linesize[0] = 0;
+                       frame->linesize[1] = 0;
+                       frame->linesize[2] = 0;
+                       frame->type = FF_BUFFER_TYPE_USER;
+                       render->state = FF_VDPAU_STATE_USED_FOR_REFERENCE;
+                       frame->reordered_opaque = codec_context->reordered_opaque;
+                       if ( frame->reference )
+                       {
+                               frame->age = this->vdpau->ip_age[0];
+                               this->vdpau->ip_age[0] = this->vdpau->ip_age[1] + 1;
+                               this->vdpau->ip_age[1] = 1;
+                               this->vdpau->b_age++;
+                       }
+                       else
+                       {
+                               frame->age = this->vdpau->b_age;
+                               this->vdpau->ip_age[0] ++;
+                               this->vdpau->ip_age[1] ++;
+                               this->vdpau->b_age = 1;
+                       }
+               }
+               else
+               {
+                       mlt_log_warning( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU surface underrun\n" );
+                       error = -1;
+               }
+       }
+       else
+       {
+               mlt_log_warning( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU surface underrun\n" );
+               error = -1;
+       }
+       
+       return error;
+}
+
+static void vdpau_release_buffer( AVCodecContext *codec_context, AVFrame *frame )
+{
+       producer_avformat this = codec_context->opaque;
+       struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0];
+       mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "vdpau_release_buffer (%x)\n", render->surface );
+       int i;
+
+       render->state &= ~FF_VDPAU_STATE_USED_FOR_REFERENCE;
+       for ( i = 0; i < 4; i++ )
+               frame->data[i] = NULL;
+       mlt_deque_push_back( this->vdpau->deque, render );
+}
+
+static void vdpau_draw_horiz( AVCodecContext *codec_context, const AVFrame *frame, int offset[4], int y, int type, int height )
+{
+       producer_avformat this = codec_context->opaque;
+       struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0];
+       VdpVideoSurface surface = render->surface;
+       VdpStatus status = vdp_decoder_render( g_vdpau->decoder, surface, (void*) &render->info,
+               render->bitstream_buffers_used, render->bitstream_buffers );
+       
+       if ( status != VDP_STATUS_OK )
+       {
+               this->vdpau->is_decoded = 0;
+               mlt_log_warning( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU failed to decode (%s)\n",
+                       vdp_get_error_string( status ) );
+       }
+       else
+       {
+               this->vdpau->is_decoded = 1;
+       }
+}
+
+static int vdpau_decoder_init( producer_avformat this )
+{
+       mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "vdpau_decoder_init\n" );
+       int success = 1;
+       
+       this->video_codec->opaque = this;
+       this->video_codec->get_buffer = vdpau_get_buffer;
+       this->video_codec->release_buffer = vdpau_release_buffer;
+       this->video_codec->draw_horiz_band = vdpau_draw_horiz;
+       this->video_codec->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
+       
+       VdpDecoderProfile profile = VDP_DECODER_PROFILE_H264_HIGH;
+       uint32_t max_references = this->video_codec->refs;
+       pthread_mutex_lock( &mlt_sdl_mutex );
+       VdpStatus status = vdp_decoder_create( g_vdpau->device,
+               profile, this->video_codec->width, this->video_codec->height, max_references, &g_vdpau->decoder );
+       pthread_mutex_unlock( &mlt_sdl_mutex );
+       
+       if ( status == VDP_STATUS_OK )
+       {
+               if ( !this->vdpau )
+               {
+                       int i, n = FFMIN( this->video_codec->refs + 1, MAX_VDPAU_SURFACES );
+       
+                       this->vdpau = calloc( 1, sizeof( *this->vdpau ) );
+                       this->vdpau->deque = mlt_deque_init();
+                       for ( i = 0; i < n; i++ )
+                       {
+                               if ( VDP_STATUS_OK == vdp_surface_create( g_vdpau->device, VDP_CHROMA_TYPE_420,
+                                       this->video_codec->width, this->video_codec->height, &this->vdpau->render_states[i].surface ) )
+                               {
+                                       mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "successfully created VDPAU surface %x\n",
+                                               this->vdpau->render_states[i].surface );
+                                       mlt_deque_push_back( this->vdpau->deque, &this->vdpau->render_states[i] );
+                               }
+                               else
+                               {
+                                       mlt_log_info( MLT_PRODUCER_SERVICE(&this->parent), "failed to create VDPAU surface %dx%d\n",
+                                               this->video_codec->width, this->video_codec->height );
+                                       while ( mlt_deque_count( this->vdpau->deque ) )
+                                       {
+                                               struct vdpau_render_state *render = mlt_deque_pop_front( this->vdpau->deque );
+                                               vdp_surface_destroy( render->surface );
+                                       }
+                                       mlt_deque_close( this->vdpau->deque );
+                                       free( this->vdpau );
+                                       this->vdpau = NULL;
+                                       vdp_decoder_destroy( g_vdpau->decoder );
+                                       g_vdpau->decoder = VDP_INVALID_HANDLE;
+                                       success = 0;
+                                       break;
+                               }
+                       }
+                       this->vdpau->b_age = this->vdpau->ip_age[0] = this->vdpau->ip_age[1] = 256*256*256*64; // magic from Avidemux
+               }
+               g_vdpau->producer = this;
+       }
+       else
+       {
+               success = 0;
+               g_vdpau->decoder = VDP_INVALID_HANDLE;
+               mlt_log_error( MLT_PRODUCER_SERVICE(&this->parent), "VDPAU failed to initialize decoder (%s)\n",
+                       vdp_get_error_string( status ) );
+       }
+       
+       return success;
+}
+
+static void vdpau_producer_close( producer_avformat this )
+{
+       if ( this->vdpau )
+       {
+               mlt_log_debug( MLT_PRODUCER_SERVICE(&this->parent), "vdpau_producer_close\n" );
+               int i;
+               for ( i = 0; i < MAX_VDPAU_SURFACES; i++ )
+               {
+                       if ( this->vdpau->render_states[i].surface != VDP_INVALID_HANDLE )
+                               vdp_surface_destroy( this->vdpau->render_states[i].surface );
+                       this->vdpau->render_states[i].surface = VDP_INVALID_HANDLE;
+               }
+               mlt_deque_close( this->vdpau->deque );
+               if ( this->vdpau->buffer )
+                       mlt_pool_release( this->vdpau->buffer );
+               this->vdpau->buffer = NULL;
+               free( this->vdpau );
+               this->vdpau = NULL;
+       }
+}
+
+static void vdpau_decoder_close( )
+{
+       mlt_log_debug( NULL, "vdpau_decoder_close (%x)\n", g_vdpau->decoder );
+       if ( g_vdpau && g_vdpau->decoder != VDP_INVALID_HANDLE )
+       {
+               vdp_decoder_destroy( g_vdpau->decoder );
+               g_vdpau->decoder = VDP_INVALID_HANDLE;
+               g_vdpau->producer = NULL;
+       }
+       
+}
+
+static void vdpau_close( void *ignored )
+{
+       mlt_log_debug( NULL, "vdpau_close\n" );
+       if ( g_vdpau )
+       {
+               vdpau_decoder_close( );
+               free( g_vdpau );
+               g_vdpau = NULL;
+       }
+}