2 * filehandler.cc -- saving DV data into different file formats
3 * Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include <framework/mlt_frame.h>
33 using std::ostringstream;
52 #include "filehandler.h"
57 FileTracker *FileTracker::instance = NULL;
59 FileTracker::FileTracker( ) : mode( CAPTURE_MOVIE_APPEND )
61 cerr << ">> Constructing File Capture tracker" << endl;
64 FileTracker::~FileTracker( )
66 cerr << ">> Destroying File Capture tracker" << endl;
69 FileTracker &FileTracker::GetInstance( )
71 if ( instance == NULL )
72 instance = new FileTracker();
77 void FileTracker::SetMode( FileCaptureMode mode )
82 FileCaptureMode FileTracker::GetMode( )
87 char *FileTracker::Get( int index )
92 void FileTracker::Add( const char *file )
94 if ( this->mode != CAPTURE_IGNORE )
96 cerr << ">>>> Registering " << file << " with the tracker" << endl;
97 list.push_back( strdup( file ) );
101 unsigned int FileTracker::Size( )
106 void FileTracker::Clear( )
110 free( list[ Size() - 1 ] );
113 this->mode = CAPTURE_MOVIE_APPEND;
116 FileHandler::FileHandler() : done( false ), autoSplit( false ), maxFrameCount( 999999 ),
117 framesWritten( 0 ), filename( "" )
127 FileHandler::~FileHandler()
133 bool FileHandler::GetAutoSplit() const
139 bool FileHandler::GetTimeStamp() const
145 string FileHandler::GetBaseName() const
151 string FileHandler::GetExtension() const
157 int FileHandler::GetMaxFrameCount() const
159 return maxFrameCount;
162 off_t FileHandler::GetMaxFileSize() const
167 string FileHandler::GetFilename() const
173 void FileHandler::SetAutoSplit( bool flag )
179 void FileHandler::SetTimeStamp( bool flag )
185 void FileHandler::SetBaseName( const string& s )
191 void FileHandler::SetMaxFrameCount( int count )
193 assert( count >= 0 );
194 maxFrameCount = count;
198 void FileHandler::SetEveryNthFrame( int every )
200 assert ( every > 0 );
202 everyNthFrame = every;
206 void FileHandler::SetMaxFileSize( off_t size )
208 assert ( size >= 0 );
214 void FileHandler::SetSampleFrame( const Frame& sample )
220 bool FileHandler::Done()
226 bool FileHandler::WriteFrame( const Frame& frame )
228 static TimeCode prevTimeCode;
231 /* If the user wants autosplit, start a new file if a
232 new recording is detected. */
233 prevTimeCode.sec = -1;
234 frame.GetTimeCode( timeCode );
235 int time_diff = timeCode.sec - prevTimeCode.sec;
236 bool discontinuity = prevTimeCode.sec != -1 && ( time_diff > 1 || ( time_diff < 0 && time_diff > -59 ) );
237 if ( FileIsOpen() && GetAutoSplit() == true && ( frame.IsNewRecording() || discontinuity ) )
242 if ( FileIsOpen() == false )
246 static int counter = 0;
248 if ( GetTimeStamp() == true )
250 ostringstream sb, sb2;
254 if ( ! frame.GetRecordingDate( date ) )
258 gettimeofday( &tv, &tz );
259 localtime_r( static_cast< const time_t * >( &tv.tv_sec ), &date );
262 << setw( 4 ) << date.tm_year + 1900 << '.'
263 << setw( 2 ) << date.tm_mon + 1 << '.'
264 << setw( 2 ) << date.tm_mday << '_'
265 << setw( 2 ) << date.tm_hour << '-'
266 << setw( 2 ) << date.tm_min << '-'
267 << setw( 2 ) << date.tm_sec;
269 sb2 << GetBaseName() << recDate << GetExtension();
270 filename = sb2.str();
271 cerr << ">>> Trying " << filename << endl;
279 sb << GetBaseName() << setfill( '0' ) << setw( 3 ) << ++ counter << GetExtension();
281 cerr << ">>> Trying " << filename << endl;
283 while ( stat( filename.c_str(), &stats ) == 0 );
286 SetSampleFrame( frame );
287 if ( Create( filename ) == false )
289 cerr << ">>> Error creating file!" << endl;
298 if ( framesToSkip == 0 )
300 if ( 0 > Write( frame ) )
302 cerr << ">>> Error writing frame!" << endl;
305 framesToSkip = everyNthFrame;
310 /* If the frame count is exceeded, close the current file.
311 If the autosplit flag is set, a new file will be created in the next iteration.
312 If the flag is not set, we are done. */
314 if ( ( GetMaxFrameCount() > 0 ) &&
315 ( framesWritten >= GetMaxFrameCount() ) )
318 done = !GetAutoSplit();
321 /* If the file size could be exceeded by another frame, close the current file.
322 If the autosplit flag is set, a new file will be created on the next iteration.
323 If the flag is not set, we are done. */
324 /* not exact, but should be good enough to prevent going over. */
328 frame.GetAudioInfo( info );
329 if ( ( GetFileSize() > 0 ) && ( GetMaxFileSize() > 0 ) &&
330 ( GetFileSize() + frame.GetFrameSize() + info.samples * 4 + 12 )
331 >= GetMaxFileSize() )
332 { // 12 = sizeof chunk metadata
334 done = !GetAutoSplit();
337 prevTimeCode.sec = timeCode.sec;
342 RawHandler::RawHandler() : fd( -1 )
349 RawHandler::~RawHandler()
355 bool RawHandler::FileIsOpen()
361 bool RawHandler::Create( const string& filename )
363 fd = open( filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_NONBLOCK, 0644 );
366 FileTracker::GetInstance().Add( filename.c_str() );
367 this->filename = filename;
374 int RawHandler::Write( const Frame& frame )
376 int result = write( fd, frame.data, frame.GetFrameSize() );
381 int RawHandler::Close()
392 off_t RawHandler::GetFileSize()
394 struct stat file_status;
395 fstat( fd, &file_status );
396 return file_status.st_size;
399 int RawHandler::GetTotalFrames()
401 return GetFileSize() / ( 480 * numBlocks );
405 bool RawHandler::Open( const char *s )
407 unsigned char data[ 4 ];
409 fd = open( s, O_RDONLY | O_NONBLOCK );
412 if ( read( fd, data, 4 ) < 0 )
414 if ( lseek( fd, 0, SEEK_SET ) < 0 )
416 numBlocks = ( ( data[ 3 ] & 0x80 ) == 0 ) ? 250 : 300;
422 int RawHandler::GetFrame( uint8_t *data, int frameNum )
425 int size = 480 * numBlocks;
428 off_t offset = ( ( off_t ) frameNum * ( off_t ) size );
429 fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
430 if ( read( fd, data, size ) > 0 )
437 /***************************************************************************/
440 AVIHandler::AVIHandler( int format ) : avi( NULL ), aviFormat( format ), isOpenDML( false ),
441 fccHandler( make_fourcc( "dvsd" ) ), channels( 2 ), isFullyInitialized( false ),
445 for ( int c = 0; c < 4; c++ )
446 audioChannels[ c ] = NULL;
447 memset( &dvinfo, 0, sizeof( dvinfo ) );
451 AVIHandler::~AVIHandler()
453 if ( audioBuffer != NULL )
458 for ( int c = 0; c < 4; c++ )
460 if ( audioChannels[ c ] != NULL )
462 delete audioChannels[ c ];
463 audioChannels[ c ] = NULL;
471 void AVIHandler::SetSampleFrame( const Frame& sample )
474 sample.GetAudioInfo( audioInfo );
475 sample.GetVideoInfo( videoInfo );
477 sample.GetAAUXPack( 0x50, pack );
478 dvinfo.dwDVAAuxSrc = *( DWORD* ) ( pack.data + 1 );
479 sample.GetAAUXPack( 0x51, pack );
480 dvinfo.dwDVAAuxCtl = *( DWORD* ) ( pack.data + 1 );
482 sample.GetAAUXPack( 0x52, pack );
483 dvinfo.dwDVAAuxSrc1 = *( DWORD* ) ( pack.data + 1 );
484 sample.GetAAUXPack( 0x53, pack );
485 dvinfo.dwDVAAuxCtl1 = *( DWORD* ) ( pack.data + 1 );
487 sample.GetVAUXPack( 0x60, pack );
488 dvinfo.dwDVVAuxSrc = *( DWORD* ) ( pack.data + 1 );
489 sample.GetVAUXPack( 0x61, pack );
490 dvinfo.dwDVVAuxCtl = *( DWORD* ) ( pack.data + 1 );
494 if ( sample.decoder->std == e_dv_std_smpte_314m )
495 fccHandler = make_fourcc( "dv25" );
500 bool AVIHandler::FileIsOpen()
506 bool AVIHandler::Create( const string& filename )
508 assert( avi == NULL );
514 fail_null( avi = new AVI1File );
515 if ( avi->Create( filename.c_str() ) == false )
517 //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency, AVI_LARGE_INDEX );
521 fail_null( avi = new AVI2File );
522 if ( avi->Create( filename.c_str() ) == false )
524 //if ( GetOpenDML() )
525 //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
526 //( AVI_SMALL_INDEX | AVI_LARGE_INDEX ) );
528 //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
529 //( AVI_SMALL_INDEX ) );
533 assert( aviFormat == AVI_DV1_FORMAT || aviFormat == AVI_DV2_FORMAT );
536 avi->setDVINFO( dvinfo );
537 avi->setFccHandler( make_fourcc( "iavs" ), fccHandler );
538 avi->setFccHandler( make_fourcc( "vids" ), fccHandler );
539 this->filename = filename;
540 FileTracker::GetInstance().Add( filename.c_str() );
541 return ( avi != NULL );
545 int AVIHandler::Write( const Frame& frame )
547 assert( avi != NULL );
550 return avi->WriteFrame( frame ) ? 0 : -1;
559 int AVIHandler::Close()
567 if ( audioBuffer != NULL )
572 for ( int c = 0; c < 4; c++ )
574 if ( audioChannels[ c ] != NULL )
576 delete audioChannels[ c ];
577 audioChannels[ c ] = NULL;
580 isFullyInitialized = false;
584 off_t AVIHandler::GetFileSize()
586 return avi->GetFileSize();
589 int AVIHandler::GetTotalFrames()
591 return avi->GetTotalFrames();
595 bool AVIHandler::Open( const char *s )
597 assert( avi == NULL );
598 fail_null( avi = new AVI1File );
599 if ( avi->Open( s ) )
603 avi->verifyStreamFormat( make_fourcc( "dvsd" ) ) ||
604 avi->verifyStreamFormat( make_fourcc( "DVSD" ) ) ||
605 avi->verifyStreamFormat( make_fourcc( "dvcs" ) ) ||
606 avi->verifyStreamFormat( make_fourcc( "DVCS" ) ) ||
607 avi->verifyStreamFormat( make_fourcc( "dvcp" ) ) ||
608 avi->verifyStreamFormat( make_fourcc( "DVCP" ) ) ||
609 avi->verifyStreamFormat( make_fourcc( "CDVC" ) ) ||
610 avi->verifyStreamFormat( make_fourcc( "cdvc" ) ) ||
611 avi->verifyStreamFormat( make_fourcc( "DV25" ) ) ||
612 avi->verifyStreamFormat( make_fourcc( "dv25" ) ) ) )
615 if ( avi->verifyStream( make_fourcc( "auds" ) ) )
616 aviFormat = AVI_DV2_FORMAT;
618 aviFormat = AVI_DV1_FORMAT;
619 isOpenDML = avi->isOpenDML();
628 int AVIHandler::GetFrame( uint8_t *data, int frameNum )
630 int result = avi->GetDVFrame( data, frameNum );
634 /* get the audio from the audio stream, if available */
635 if ( aviFormat == AVI_DV2_FORMAT )
639 if ( ! isFullyInitialized &&
640 avi->getStreamFormat( ( void* ) &wav, make_fourcc( "auds" ) ) )
642 if ( channels > 0 && channels < 5 )
644 // Allocate interleaved audio buffer
645 audioBuffer = new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES * channels ];
647 // Allocate non-interleaved audio buffers
648 for ( int c = 0; c < channels; c++ )
649 audioChannels[ c ] = new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES ];
651 // Get the audio parameters from AVI for subsequent calls to method
652 audioInfo.channels = wav.nChannels;
653 audioInfo.frequency = wav.nSamplesPerSec;
655 // Skip initialization on subsequent calls to method
656 isFullyInitialized = true;
657 cerr << ">>> using audio from separate AVI audio stream" << endl;
661 // Get the frame from AVI
662 int n = avi->getFrame( audioBuffer, frameNum, make_fourcc( "01wb" ) );
665 // Temporary pointer to audio scratch buffer
666 int16_t * s = audioBuffer;
668 // Determine samples in this frame
669 audioInfo.samples = n / audioInfo.channels / sizeof( int16_t );
671 // Convert interleaved audio into non-interleaved
672 for ( int n = 0; n < audioInfo.samples; ++n )
673 for ( int i = 0; i < audioInfo.channels; i++ )
674 audioChannels[ i ][ n ] = *s++;
676 // Write interleaved audio into frame
677 frame.EncodeAudio( audioInfo, audioChannels );
681 // Parse important metadata in DV bitstream
682 frame.ExtractHeader();
689 void AVIHandler::SetOpenDML( bool flag )
695 bool AVIHandler::GetOpenDML() const
701 /***************************************************************************/
703 #ifdef HAVE_LIBQUICKTIME
706 #define DV_AUDIO_MAX_SAMPLES 1944
709 // Missing fourcc's in libquicktime (allows compilation)
710 #ifndef QUICKTIME_DV_AVID
711 #define QUICKTIME_DV_AVID "AVdv"
714 #ifndef QUICKTIME_DV_AVID_A
715 #define QUICKTIME_DV_AVID_A "dvcp"
718 #ifndef QUICKTIME_DVCPRO
719 #define QUICKTIME_DVCPRO "dvpp"
722 QtHandler::QtHandler() : fd( NULL )
729 QtHandler::~QtHandler()
734 void QtHandler::Init()
741 samplesPerBuffer = 0;
744 audioChannelBuffer = NULL;
745 isFullyInitialized = false;
749 bool QtHandler::FileIsOpen()
755 bool QtHandler::Create( const string& filename )
759 if ( open( filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_NONBLOCK, 0644 ) != -1 )
761 fd = quicktime_open( const_cast<char*>( filename.c_str() ), 0, 1 );
763 FileTracker::GetInstance().Add( filename.c_str() );
767 this->filename = filename;
771 void QtHandler::AllocateAudioBuffers()
773 if ( channels > 0 && channels < 5 )
775 audioBufferSize = DV_AUDIO_MAX_SAMPLES * 2;
776 audioBuffer = new int16_t[ audioBufferSize * channels ];
778 audioChannelBuffer = new short int * [ channels ];
779 for ( int c = 0; c < channels; c++ )
780 audioChannelBuffer[ c ] = new short int[ audioBufferSize ];
781 isFullyInitialized = true;
785 inline void QtHandler::DeinterlaceStereo16( void* pInput, int iBytes,
786 void* pLOutput, void* pROutput )
788 short int * piSampleInput = ( short int* ) pInput;
789 short int* piSampleLOutput = ( short int* ) pLOutput;
790 short int* piSampleROutput = ( short int* ) pROutput;
792 while ( ( char* ) piSampleInput < ( ( char* ) pInput + iBytes ) )
794 *piSampleLOutput++ = *piSampleInput++;
795 *piSampleROutput++ = *piSampleInput++;
800 int QtHandler::Write( const Frame& frame )
802 if ( ! isFullyInitialized )
806 if ( frame.GetAudioInfo( audio ) )
809 quicktime_set_audio( fd, channels, audio.frequency, 16, QUICKTIME_TWOS );
816 quicktime_set_video( fd, 1, 720, frame.IsPAL() ? 576 : 480,
817 frame.GetFrameRate(), QUICKTIME_DV );
818 AllocateAudioBuffers();
821 int result = quicktime_write_frame( fd, const_cast<unsigned char*>( frame.data ),
822 frame.GetFrameSize(), 0 );
827 if ( frame.GetAudioInfo( audio ) && ( unsigned int ) audio.samples < audioBufferSize )
829 long bytesRead = frame.ExtractAudio( audioBuffer );
831 DeinterlaceStereo16( audioBuffer, bytesRead,
832 audioChannelBuffer[ 0 ],
833 audioChannelBuffer[ 1 ] );
835 quicktime_encode_audio( fd, audioChannelBuffer, NULL, audio.samples );
842 int QtHandler::Close()
846 quicktime_close( fd );
849 if ( audioBuffer != NULL )
854 if ( audioChannelBuffer != NULL )
856 for ( int c = 0; c < channels; c++ )
857 delete audioChannelBuffer[ c ];
858 delete audioChannelBuffer;
859 audioChannelBuffer = NULL;
865 off_t QtHandler::GetFileSize()
867 struct stat file_status;
868 stat( filename.c_str(), &file_status );
869 return file_status.st_size;
873 int QtHandler::GetTotalFrames()
875 return ( int ) quicktime_video_length( fd, 0 );
879 bool QtHandler::Open( const char *s )
883 fd = quicktime_open( s, 1, 0 );
886 fprintf( stderr, "Error opening: %s\n", s );
890 if ( quicktime_has_video( fd ) <= 0 )
892 fprintf( stderr, "There must be at least one video track in the input file (%s).\n",
897 char * fcc = quicktime_video_compressor( fd, 0 );
898 if ( strncmp( fcc, QUICKTIME_DV, 4 ) != 0 &&
899 strncmp( fcc, QUICKTIME_DV_AVID, 4 ) != 0 &&
900 strncmp( fcc, QUICKTIME_DV_AVID_A, 4 ) != 0 &&
901 strncmp( fcc, QUICKTIME_DVCPRO, 4 ) != 0 )
906 if ( quicktime_has_audio( fd ) )
907 channels = quicktime_track_channels( fd, 0 );
912 int QtHandler::GetFrame( uint8_t *data, int frameNum )
914 assert( fd != NULL );
916 quicktime_set_video_position( fd, frameNum, 0 );
917 quicktime_read_frame( fd, data, 0 );
920 if ( quicktime_has_audio( fd ) )
922 if ( ! isFullyInitialized )
923 AllocateAudioBuffers();
925 // Fetch the frequency of the audio track and calc number of samples needed
926 int frequency = quicktime_sample_rate( fd, 0 );
927 float fps = ( data[ 3 ] & 0x80 ) ? 25.0f : 29.97f;
928 int samples = mlt_sample_calculator( fps, frequency, frameNum );
929 int64_t seek = mlt_sample_calculator_to_now( fps, frequency, frameNum );
931 // Obtain a dv encoder and initialise it with minimal info
932 dv_encoder_t *encoder = dv_encoder_new( 0, 0, 0 );
933 encoder->isPAL = ( data[ 3 ] & 0x80 );
934 encoder->samples_this_frame = samples;
936 // Seek to the calculated position and decode
937 quicktime_set_audio_position( fd, seek, 0 );
938 lqt_decode_audio( fd, audioChannelBuffer, NULL, (long) samples );
940 // Encode the audio on the frame and done
941 dv_encode_full_audio( encoder, audioChannelBuffer, channels, frequency, data );
942 dv_encoder_free( encoder );