]> git.sesse.net Git - mlt/blobdiff - src/modules/kino/filehandler.cc
Initial version
[mlt] / src / modules / kino / filehandler.cc
diff --git a/src/modules/kino/filehandler.cc b/src/modules/kino/filehandler.cc
new file mode 100644 (file)
index 0000000..740ee56
--- /dev/null
@@ -0,0 +1,913 @@
+/*
+* filehandler.cc -- saving DV data into different file formats
+* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "config.h"
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+
+using std::cerr;
+using std::endl;
+using std::ostringstream;
+using std::setw;
+using std::setfill;
+
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "filehandler.h"
+#include "error.h"
+#include "riff.h"
+#include "avi.h"
+
+FileTracker *FileTracker::instance = NULL;
+
+FileTracker::FileTracker( ) : mode( CAPTURE_MOVIE_APPEND )
+{
+       cerr << ">> Constructing File Capture tracker" << endl;
+}
+
+FileTracker::~FileTracker( )
+{
+       cerr << ">> Destroying File Capture tracker" << endl;
+}
+
+FileTracker &FileTracker::GetInstance( )
+{
+       if ( instance == NULL )
+               instance = new FileTracker();
+
+       return *instance;
+}
+
+void FileTracker::SetMode( FileCaptureMode mode )
+{
+       this->mode = mode;
+}
+
+FileCaptureMode FileTracker::GetMode( )
+{
+       return this->mode;
+}
+
+char *FileTracker::Get( int index )
+{
+       return list[ index ];
+}
+
+void FileTracker::Add( const char *file )
+{
+       if ( this->mode != CAPTURE_IGNORE )
+       {
+               cerr << ">>>> Registering " << file << " with the tracker" << endl;
+               list.push_back( strdup( file ) );
+       }
+}
+
+unsigned int FileTracker::Size( )
+{
+       return list.size();
+}
+
+void FileTracker::Clear( )
+{
+       while ( Size() > 0 )
+       {
+               free( list[ Size() - 1 ] );
+               list.pop_back( );
+       }
+       this->mode = CAPTURE_MOVIE_APPEND;
+}
+
+FileHandler::FileHandler() : done( false ), autoSplit( false ), maxFrameCount( 999999 ),
+               framesWritten( 0 ), filename( "" )
+{
+       /* empty body */
+}
+
+
+FileHandler::~FileHandler()
+{
+       /* empty body */
+}
+
+
+bool FileHandler::GetAutoSplit() const
+{
+       return autoSplit;
+}
+
+
+bool FileHandler::GetTimeStamp() const
+{
+       return timeStamp;
+}
+
+
+string FileHandler::GetBaseName() const
+{
+       return base;
+}
+
+
+string FileHandler::GetExtension() const
+{
+       return extension;
+}
+
+
+int FileHandler::GetMaxFrameCount() const
+{
+       return maxFrameCount;
+}
+
+off_t FileHandler::GetMaxFileSize() const
+{
+       return maxFileSize;
+}
+
+string FileHandler::GetFilename() const
+{
+       return filename;
+}
+
+
+void FileHandler::SetAutoSplit( bool flag )
+{
+       autoSplit = flag;
+}
+
+
+void FileHandler::SetTimeStamp( bool flag )
+{
+       timeStamp = flag;
+}
+
+
+void FileHandler::SetBaseName( const string& s )
+{
+       base = s;
+}
+
+
+void FileHandler::SetMaxFrameCount( int count )
+{
+       assert( count >= 0 );
+       maxFrameCount = count;
+}
+
+
+void FileHandler::SetEveryNthFrame( int every )
+{
+       assert ( every > 0 );
+
+       everyNthFrame = every;
+}
+
+
+void FileHandler::SetMaxFileSize( off_t size )
+{
+       assert ( size >= 0 );
+       maxFileSize = size;
+}
+
+
+#if 0
+void FileHandler::SetSampleFrame( const Frame& sample )
+{
+       /* empty body */
+}
+#endif
+
+bool FileHandler::Done()
+{
+       return done;
+}
+
+#if 0
+bool FileHandler::WriteFrame( const Frame& frame )
+{
+       static TimeCode prevTimeCode;
+       TimeCode timeCode;
+
+       /* If the user wants autosplit, start a new file if a
+          new recording is detected. */
+       prevTimeCode.sec = -1;
+       frame.GetTimeCode( timeCode );
+       int time_diff = timeCode.sec - prevTimeCode.sec;
+       bool discontinuity = prevTimeCode.sec != -1 && ( time_diff > 1 || ( time_diff < 0 && time_diff > -59 ) );
+       if ( FileIsOpen() && GetAutoSplit() == true && ( frame.IsNewRecording() || discontinuity ) )
+       {
+               Close();
+       }
+
+       if ( FileIsOpen() == false )
+       {
+
+               string filename;
+               static int counter = 0;
+
+               if ( GetTimeStamp() == true )
+               {
+                       ostringstream sb, sb2;
+                       struct tm       date;
+                       string  recDate;
+
+                       if ( ! frame.GetRecordingDate( date ) )
+                       {
+                               struct timeval tv;
+                               struct timezone tz;
+                               gettimeofday( &tv, &tz );
+                               localtime_r( static_cast< const time_t * >( &tv.tv_sec ), &date );
+                       }
+                       sb << setfill( '0' )
+                       << setw( 4 ) << date.tm_year + 1900 << '.'
+                       << setw( 2 ) << date.tm_mon + 1 << '.'
+                       << setw( 2 ) << date.tm_mday << '_'
+                       << setw( 2 ) << date.tm_hour << '-'
+                       << setw( 2 ) << date.tm_min << '-'
+                       << setw( 2 ) << date.tm_sec;
+                       recDate = sb.str();
+                       sb2 << GetBaseName() << recDate << GetExtension();
+                       filename = sb2.str();
+                       cerr << ">>> Trying " << filename << endl;
+               }
+               else
+               {
+                       struct stat stats;
+                       do
+                       {
+                               ostringstream sb;
+                               sb << GetBaseName() << setfill( '0' ) << setw( 3 ) << ++ counter << GetExtension();
+                               filename = sb.str();
+                               cerr << ">>> Trying " << filename << endl;
+                       }
+                       while ( stat( filename.c_str(), &stats ) == 0 );
+               }
+
+               SetSampleFrame( frame );
+               if ( Create( filename ) == false )
+               {
+                       cerr << ">>> Error creating file!" << endl;
+                       return false;
+               }
+               framesWritten = 0;
+               framesToSkip = 0;
+       }
+
+       /* write frame */
+
+       if ( framesToSkip == 0 )
+       {
+               if ( 0 > Write( frame ) )
+               {
+                       cerr << ">>> Error writing frame!" << endl;
+                       return false;
+               }
+               framesToSkip = everyNthFrame;
+               ++framesWritten;
+       }
+       framesToSkip--;
+
+       /* If the frame count is exceeded, close the current file.
+          If the autosplit flag is set, a new file will be created in the next iteration.
+          If the flag is not set, we are done. */
+
+       if ( ( GetMaxFrameCount() > 0 ) &&
+               ( framesWritten >= GetMaxFrameCount() ) )
+       {
+               Close();
+               done = !GetAutoSplit();
+       }
+
+       /* If the file size could be exceeded by another frame, close the current file.
+          If the autosplit flag is set, a new file will be created on the next iteration.
+          If the flag is not set, we are done. */
+       /* not exact, but should be good enough to prevent going over. */
+       if ( FileIsOpen() )
+       {
+               AudioInfo info;
+               frame.GetAudioInfo( info );
+               if ( ( GetFileSize() > 0 ) && ( GetMaxFileSize() > 0 ) &&
+                       ( GetFileSize() + frame.GetFrameSize() + info.samples * 4 + 12 )
+                       >= GetMaxFileSize() )
+               {                     // 12 = sizeof chunk metadata
+                       Close();
+                       done = !GetAutoSplit();
+               }
+       }
+    prevTimeCode.sec = timeCode.sec;
+       return true;
+}
+#endif
+
+RawHandler::RawHandler() : fd( -1 )
+{
+       extension = ".dv";
+}
+
+
+RawHandler::~RawHandler()
+{
+       Close();
+}
+
+
+bool RawHandler::FileIsOpen()
+{
+       return fd != -1;
+}
+
+
+bool RawHandler::Create( const string& filename )
+{
+       fd = open( filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_NONBLOCK, 0644 );
+       if ( fd != -1 )
+       {
+               FileTracker::GetInstance().Add( filename.c_str() );
+               this->filename = filename;
+       }
+       return ( fd != -1 );
+}
+
+
+#if 0
+int RawHandler::Write( const Frame& frame )
+{
+       int result = write( fd, frame.data, frame.GetFrameSize() );
+       return result;
+}
+#endif
+
+int RawHandler::Close()
+{
+       if ( fd != -1 )
+       {
+               close( fd );
+               fd = -1;
+       }
+       return 0;
+}
+
+
+off_t RawHandler::GetFileSize()
+{
+       struct stat file_status;
+       fstat( fd, &file_status );
+       return file_status.st_size;
+}
+
+int RawHandler::GetTotalFrames()
+{
+       return GetFileSize() / ( 480 * numBlocks );
+}
+
+
+bool RawHandler::Open( const char *s )
+{
+       unsigned char data[ 4 ];
+       assert( fd == -1 );
+       fd = open( s, O_RDONLY | O_NONBLOCK );
+       if ( fd < 0 )
+               return false;
+       if ( read( fd, data, 4 ) < 0 )
+               return false;
+       lseek( fd, 0, SEEK_SET );
+       numBlocks = ( ( data[ 3 ] & 0x80 ) == 0 ) ? 250 : 300;
+       filename = s;
+       return true;
+
+}
+
+int RawHandler::GetFrame( uint8_t *data, int frameNum )
+{
+       assert( fd != -1 );
+       int size = 480 * numBlocks;
+       if ( frameNum < 0 )
+               return -1;
+       off_t offset = ( ( off_t ) frameNum * ( off_t ) size );
+       fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
+       if ( read( fd, data, size ) > 0 )
+               return 0;
+       else
+               return -1;
+}
+
+
+/***************************************************************************/
+
+
+AVIHandler::AVIHandler( int format ) : avi( NULL ), aviFormat( format ), isOpenDML( false ),
+               fccHandler( make_fourcc( "dvsd" ) ), channels( 2 ), isFullyInitialized( false ),
+               audioBuffer( NULL )
+{
+       extension = ".avi";
+       for ( int c = 0; c < 4; c++ )
+               audioChannels[ c ] = NULL;
+}
+
+
+AVIHandler::~AVIHandler()
+{
+       if ( audioBuffer != NULL )
+       {
+               delete audioBuffer;
+               audioBuffer = NULL;
+       }
+       for ( int c = 0; c < 4; c++ )
+       {
+               if ( audioChannels[ c ] != NULL )
+               {
+                       delete audioChannels[ c ];
+                       audioChannels[ c ] = NULL;
+               }
+       }
+
+       delete avi;
+}
+
+#if 0
+void AVIHandler::SetSampleFrame( const Frame& sample )
+{
+       Pack pack;
+       sample.GetAudioInfo( audioInfo );
+       sample.GetVideoInfo( videoInfo );
+
+       sample.GetAAUXPack( 0x50, pack );
+       dvinfo.dwDVAAuxSrc = *( DWORD* ) ( pack.data + 1 );
+       sample.GetAAUXPack( 0x51, pack );
+       dvinfo.dwDVAAuxCtl = *( DWORD* ) ( pack.data + 1 );
+
+       sample.GetAAUXPack( 0x52, pack );
+       dvinfo.dwDVAAuxSrc1 = *( DWORD* ) ( pack.data + 1 );
+       sample.GetAAUXPack( 0x53, pack );
+       dvinfo.dwDVAAuxCtl1 = *( DWORD* ) ( pack.data + 1 );
+
+       sample.GetVAUXPack( 0x60, pack );
+       dvinfo.dwDVVAuxSrc = *( DWORD* ) ( pack.data + 1 );
+       sample.GetVAUXPack( 0x61, pack );
+       dvinfo.dwDVVAuxCtl = *( DWORD* ) ( pack.data + 1 );
+
+#ifdef WITH_LIBDV
+
+       if ( sample.decoder->std == e_dv_std_smpte_314m )
+               fccHandler = make_fourcc( "dv25" );
+#endif
+}
+#endif
+
+bool AVIHandler::FileIsOpen()
+{
+       return avi != NULL;
+}
+
+
+bool AVIHandler::Create( const string& filename )
+{
+       assert( avi == NULL );
+
+       switch ( aviFormat )
+       {
+
+       case AVI_DV1_FORMAT:
+               fail_null( avi = new AVI1File );
+               if ( avi->Create( filename.c_str() ) == false )
+                       return false;
+               //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency, AVI_LARGE_INDEX );
+               break;
+
+       case AVI_DV2_FORMAT:
+               fail_null( avi = new AVI2File );
+               if ( avi->Create( filename.c_str() ) == false )
+                       return false;
+               //if ( GetOpenDML() )
+                       //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
+                                  //( AVI_SMALL_INDEX | AVI_LARGE_INDEX ) );
+               //else
+                       //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
+                                  //( AVI_SMALL_INDEX ) );
+               break;
+
+       default:
+               assert( aviFormat == AVI_DV1_FORMAT || aviFormat == AVI_DV2_FORMAT );
+       }
+
+       avi->setDVINFO( dvinfo );
+       avi->setFccHandler( make_fourcc( "iavs" ), fccHandler );
+       avi->setFccHandler( make_fourcc( "vids" ), fccHandler );
+       this->filename = filename;
+       FileTracker::GetInstance().Add( filename.c_str() );
+       return ( avi != NULL );
+}
+
+#if 0
+int AVIHandler::Write( const Frame& frame )
+{
+       assert( avi != NULL );
+       try
+       {
+               return avi->WriteFrame( frame ) ? 0 : -1;
+       }
+       catch (...)
+       {
+               return -1;
+       }
+}
+#endif
+
+int AVIHandler::Close()
+{
+       if ( avi != NULL )
+       {
+               avi->WriteRIFF();
+               delete avi;
+               avi = NULL;
+       }
+       if ( audioBuffer != NULL )
+       {
+               delete audioBuffer;
+               audioBuffer = NULL;
+       }
+       for ( int c = 0; c < 4; c++ )
+       {
+               if ( audioChannels[ c ] != NULL )
+               {
+                       delete audioChannels[ c ];
+                       audioChannels[ c ] = NULL;
+               }
+       }
+       isFullyInitialized = false;
+       return 0;
+}
+
+off_t AVIHandler::GetFileSize()
+{
+       return avi->GetFileSize();
+}
+
+int AVIHandler::GetTotalFrames()
+{
+       return avi->GetTotalFrames();
+}
+
+
+bool AVIHandler::Open( const char *s )
+{
+       assert( avi == NULL );
+       fail_null( avi = new AVI1File );
+       if ( avi->Open( s ) )
+       {
+               avi->ParseRIFF();
+               if ( ! (
+                           avi->verifyStreamFormat( make_fourcc( "dvsd" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "DVSD" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "dvcs" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "DVCS" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "dvcp" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "DVCP" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "CDVC" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "cdvc" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "DV25" ) ) ||
+                           avi->verifyStreamFormat( make_fourcc( "dv25" ) ) ) )
+                       return false;
+               avi->ReadIndex();
+               if ( avi->verifyStream( make_fourcc( "auds" ) ) )
+                       aviFormat = AVI_DV2_FORMAT;
+               else
+                       aviFormat = AVI_DV1_FORMAT;
+               isOpenDML = avi->isOpenDML();
+               filename = s;
+               return true;
+       }
+       else
+               return false;
+
+}
+
+int AVIHandler::GetFrame( uint8_t *data, int frameNum )
+{
+       int result = avi->GetDVFrame( data, frameNum );
+#if 0
+       if ( result == 0 )
+       {
+               /* get the audio from the audio stream, if available */
+               if ( aviFormat == AVI_DV2_FORMAT )
+               {
+                       WAVEFORMATEX wav;
+
+                       if ( ! isFullyInitialized && 
+                                avi->getStreamFormat( ( void* ) &wav, make_fourcc( "auds" ) ) )
+                       {
+                               if ( channels > 0 && channels < 5 )
+                               {
+                                       // Allocate interleaved audio buffer
+                                       audioBuffer = new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES * channels ];
+
+                                       // Allocate non-interleaved audio buffers
+                                       for ( int c = 0; c < channels; c++ )
+                                               audioChannels[ c ] = new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES ];
+                                       
+                                       // Get the audio parameters from AVI for subsequent calls to method
+                                       audioInfo.channels = wav.nChannels;
+                                       audioInfo.frequency = wav.nSamplesPerSec;
+
+                                       // Skip initialization on subsequent calls to method
+                                       isFullyInitialized = true;
+                                       cerr << ">>> using audio from separate AVI audio stream" << endl;
+                               }
+                       }
+
+                       // Get the frame from AVI
+                       int n = avi->getFrame( audioBuffer, frameNum, make_fourcc( "01wb" ) );
+                       if ( n > 0 )
+                       {
+                               // Temporary pointer to audio scratch buffer
+                               int16_t * s = audioBuffer;
+
+                               // Determine samples in this frame
+                               audioInfo.samples = n / audioInfo.channels / sizeof( int16_t );
+                               
+                               // Convert interleaved audio into non-interleaved
+                               for ( int n = 0; n < audioInfo.samples; ++n )
+                                       for ( int i = 0; i < audioInfo.channels; i++ )
+                                               audioChannels[ i ][ n ] = *s++;
+
+                               // Write interleaved audio into frame
+                               frame.EncodeAudio( audioInfo, audioChannels );
+                       }
+               }
+
+               // Parse important metadata in DV bitstream
+               frame.ExtractHeader();
+       }
+#endif
+       return result;
+}
+
+
+void AVIHandler::SetOpenDML( bool flag )
+{
+       isOpenDML = flag;
+}
+
+
+bool AVIHandler::GetOpenDML() const
+{
+       return isOpenDML;
+}
+
+
+/***************************************************************************/
+
+#ifdef HAVE_LIBQUICKTIME
+
+#ifndef HAVE_LIBDV
+#define DV_AUDIO_MAX_SAMPLES 1944
+#endif
+
+QtHandler::QtHandler() : fd( NULL )
+{
+       extension = ".mov";
+       Init();
+}
+
+
+QtHandler::~QtHandler()
+{
+       Close();
+}
+
+void QtHandler::Init()
+{
+       if ( fd != NULL )
+               Close();
+
+       fd = NULL;
+       samplingRate = 0;
+       samplesPerBuffer = 0;
+       channels = 2;
+       audioBuffer = NULL;
+       audioChannelBuffer = NULL;
+       isFullyInitialized = false;
+}
+
+
+bool QtHandler::FileIsOpen()
+{
+       return fd != NULL;
+}
+
+
+bool QtHandler::Create( const string& filename )
+{
+       Init();
+
+       if ( open( filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_NONBLOCK, 0644 ) != -1 )
+       {
+               fd = quicktime_open( const_cast<char*>( filename.c_str() ), 0, 1 );
+               if ( fd != NULL )
+                       FileTracker::GetInstance().Add( filename.c_str() );
+       }
+       else
+               return false;
+       this->filename = filename;
+       return true;
+}
+
+void QtHandler::AllocateAudioBuffers()
+{
+       if ( channels > 0 && channels < 5 )
+       {
+               audioBufferSize = DV_AUDIO_MAX_SAMPLES * 2;
+               audioBuffer = new int16_t[ audioBufferSize * channels ];
+
+               audioChannelBuffer = new short int * [ channels ];
+               for ( int c = 0; c < channels; c++ )
+                       audioChannelBuffer[ c ] = new short int[ audioBufferSize ];
+               isFullyInitialized = true;
+       }
+}
+
+inline void QtHandler::DeinterlaceStereo16( void* pInput, int iBytes,
+        void* pLOutput, void* pROutput )
+{
+       short int * piSampleInput = ( short int* ) pInput;
+       short int* piSampleLOutput = ( short int* ) pLOutput;
+       short int* piSampleROutput = ( short int* ) pROutput;
+
+       while ( ( char* ) piSampleInput < ( ( char* ) pInput + iBytes ) )
+       {
+               *piSampleLOutput++ = *piSampleInput++;
+               *piSampleROutput++ = *piSampleInput++;
+       }
+}
+
+#if 0
+int QtHandler::Write( const Frame& frame )
+{
+       if ( ! isFullyInitialized )
+       {
+               AudioInfo audio;
+
+               if ( frame.GetAudioInfo( audio ) )
+               {
+                       channels = 2;
+                       quicktime_set_audio( fd, channels, audio.frequency, 16, QUICKTIME_TWOS );
+               }
+               else
+               {
+                       channels = 0;
+               }
+
+               quicktime_set_video( fd, 1, 720, frame.IsPAL() ? 576 : 480,
+                                    frame.GetFrameRate(), QUICKTIME_DV );
+               AllocateAudioBuffers();
+       }
+
+       int result = quicktime_write_frame( fd, const_cast<unsigned char*>( frame.data ),
+                                           frame.GetFrameSize(), 0 );
+
+       if ( channels > 0 )
+       {
+               AudioInfo audio;
+               if ( frame.GetAudioInfo( audio ) && ( unsigned int ) audio.samples < audioBufferSize )
+               {
+                       long bytesRead = frame.ExtractAudio( audioBuffer );
+
+                       DeinterlaceStereo16( audioBuffer, bytesRead,
+                                            audioChannelBuffer[ 0 ],
+                                            audioChannelBuffer[ 1 ] );
+
+                       quicktime_encode_audio( fd, audioChannelBuffer, NULL, audio.samples );
+               }
+       }
+       return result;
+}
+#endif
+
+int QtHandler::Close()
+{
+       if ( fd != NULL )
+       {
+               quicktime_close( fd );
+               fd = NULL;
+       }
+       if ( audioBuffer != NULL )
+       {
+               delete audioBuffer;
+               audioBuffer = NULL;
+       }
+       if ( audioChannelBuffer != NULL )
+       {
+               for ( int c = 0; c < channels; c++ )
+                       delete audioChannelBuffer[ c ];
+               delete audioChannelBuffer;
+               audioChannelBuffer = NULL;
+       }
+       return 0;
+}
+
+
+off_t QtHandler::GetFileSize()
+{
+       struct stat file_status;
+       fstat( fileno( fd->stream ), &file_status );
+       return file_status.st_size;
+}
+
+
+int QtHandler::GetTotalFrames()
+{
+       return ( int ) quicktime_video_length( fd, 0 );
+}
+
+
+bool QtHandler::Open( const char *s )
+{
+       Init();
+
+       fd = quicktime_open( ( char * ) s, 1, 0 );
+       if ( fd == NULL )
+       {
+               fprintf( stderr, "Error opening: %s\n", s );
+               return false;
+       }
+
+       if ( quicktime_has_video( fd ) <= 0 )
+       {
+               fprintf( stderr, "There must be at least one video track in the input file (%s).\n",
+                        s );
+               Close();
+               return false;
+       }
+       if ( strncmp( quicktime_video_compressor( fd, 0 ), QUICKTIME_DV, 4 ) != 0 )
+       {
+               fprintf( stderr, "Video in input file (%s) must be in DV format.\n", s );
+               Close();
+               return false;
+       }
+       if ( quicktime_has_audio( fd ) )
+               channels = quicktime_track_channels( fd, 0 );
+       filename = s;
+       return true;
+}
+
+int QtHandler::GetFrame( uint8_t *data, int frameNum )
+{
+       assert( fd != NULL );
+
+       quicktime_set_video_position( fd, frameNum, 0 );
+       quicktime_read_frame( fd, data, 0 );
+
+#if 0
+       if ( quicktime_has_audio( fd ) )
+       {
+               AudioInfo info;
+               double samples;
+
+               if ( ! isFullyInitialized )
+               {
+                       cerr << ">>> using audio from separarate Quicktime audio track" << endl;
+                       AllocateAudioBuffers();
+               }
+
+               info.channels = channels;
+               info.frequency = quicktime_sample_rate( fd, 0 );
+               samples = info.frequency / quicktime_frame_rate( fd, 0 );
+               info.samples = (int) samples;
+               for ( int i = 0; i < channels; i++ )
+               {
+                       quicktime_set_audio_position( fd, ( int64_t )( frameNum * samples ), 0 );
+                       quicktime_decode_audio( fd, audioChannelBuffer[ i ], NULL, (long) samples, i );
+               }
+               frame.EncodeAudio( info, audioChannelBuffer );
+       }
+       frame.ExtractHeader();
+#endif
+
+       return 0;
+}
+#endif