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
;
50 #include "filehandler.h"
55 FileTracker
*FileTracker
::instance
= NULL
;
57 FileTracker
::FileTracker( ) : mode( CAPTURE_MOVIE_APPEND
)
59 cerr
<< ">> Constructing File Capture tracker" << endl
;
62 FileTracker
::~FileTracker( )
64 cerr
<< ">> Destroying File Capture tracker" << endl
;
67 FileTracker
&FileTracker
::GetInstance( )
69 if ( instance
== NULL
)
70 instance
= new FileTracker();
75 void FileTracker
::SetMode( FileCaptureMode mode
)
80 FileCaptureMode FileTracker
::GetMode( )
85 char *FileTracker
::Get( int index
)
90 void FileTracker
::Add( const char *file
)
92 if ( this->mode
!= CAPTURE_IGNORE
)
94 cerr
<< ">>>> Registering " << file
<< " with the tracker" << endl
;
95 list
.push_back( strdup( file
) );
99 unsigned int FileTracker
::Size( )
104 void FileTracker
::Clear( )
108 free( list
[ Size() - 1 ] );
111 this->mode
= CAPTURE_MOVIE_APPEND
;
114 FileHandler
::FileHandler() : done( false ), autoSplit( false ), maxFrameCount( 999999 ),
115 framesWritten( 0 ), filename( "" )
121 FileHandler
::~FileHandler()
127 bool FileHandler
::GetAutoSplit() const
133 bool FileHandler
::GetTimeStamp() const
139 string FileHandler
::GetBaseName() const
145 string FileHandler
::GetExtension() const
151 int FileHandler
::GetMaxFrameCount() const
153 return maxFrameCount
;
156 off_t FileHandler
::GetMaxFileSize() const
161 string FileHandler
::GetFilename() const
167 void FileHandler
::SetAutoSplit( bool flag
)
173 void FileHandler
::SetTimeStamp( bool flag
)
179 void FileHandler
::SetBaseName( const string
& s
)
185 void FileHandler
::SetMaxFrameCount( int count
)
187 assert( count
>= 0 );
188 maxFrameCount
= count
;
192 void FileHandler
::SetEveryNthFrame( int every
)
194 assert ( every
> 0 );
196 everyNthFrame
= every
;
200 void FileHandler
::SetMaxFileSize( off_t size
)
202 assert ( size
>= 0 );
208 void FileHandler
::SetSampleFrame( const Frame
& sample
)
214 bool FileHandler
::Done()
220 bool FileHandler
::WriteFrame( const Frame
& frame
)
222 static TimeCode prevTimeCode
;
225 /* If the user wants autosplit, start a new file if a
226 new recording is detected. */
227 prevTimeCode
.sec
= -1;
228 frame
.GetTimeCode( timeCode
);
229 int time_diff
= timeCode
.sec
- prevTimeCode
.sec
;
230 bool discontinuity
= prevTimeCode
.sec
!= -1 && ( time_diff
> 1 || ( time_diff
< 0 && time_diff
> -59 ) );
231 if ( FileIsOpen() && GetAutoSplit() == true && ( frame
.IsNewRecording() || discontinuity
) )
236 if ( FileIsOpen() == false )
240 static int counter
= 0;
242 if ( GetTimeStamp() == true )
244 ostringstream sb
, sb2
;
248 if ( ! frame
.GetRecordingDate( date
) )
252 gettimeofday( &tv
, &tz
);
253 localtime_r( static_cast< const time_t * >( &tv
.tv_sec
), &date
);
256 << setw( 4 ) << date
.tm_year
+ 1900 << '.'
257 << setw( 2 ) << date
.tm_mon
+ 1 << '.'
258 << setw( 2 ) << date
.tm_mday
<< '_'
259 << setw( 2 ) << date
.tm_hour
<< '-'
260 << setw( 2 ) << date
.tm_min
<< '-'
261 << setw( 2 ) << date
.tm_sec
;
263 sb2
<< GetBaseName() << recDate
<< GetExtension();
264 filename
= sb2
.str();
265 cerr
<< ">>> Trying " << filename
<< endl
;
273 sb
<< GetBaseName() << setfill( '0' ) << setw( 3 ) << ++ counter
<< GetExtension();
275 cerr
<< ">>> Trying " << filename
<< endl
;
277 while ( stat( filename
.c_str(), &stats
) == 0 );
280 SetSampleFrame( frame
);
281 if ( Create( filename
) == false )
283 cerr
<< ">>> Error creating file!" << endl
;
292 if ( framesToSkip
== 0 )
294 if ( 0 > Write( frame
) )
296 cerr
<< ">>> Error writing frame!" << endl
;
299 framesToSkip
= everyNthFrame
;
304 /* If the frame count is exceeded, close the current file.
305 If the autosplit flag is set, a new file will be created in the next iteration.
306 If the flag is not set, we are done. */
308 if ( ( GetMaxFrameCount() > 0 ) &&
309 ( framesWritten
>= GetMaxFrameCount() ) )
312 done
= !GetAutoSplit();
315 /* If the file size could be exceeded by another frame, close the current file.
316 If the autosplit flag is set, a new file will be created on the next iteration.
317 If the flag is not set, we are done. */
318 /* not exact, but should be good enough to prevent going over. */
322 frame
.GetAudioInfo( info
);
323 if ( ( GetFileSize() > 0 ) && ( GetMaxFileSize() > 0 ) &&
324 ( GetFileSize() + frame
.GetFrameSize() + info
.samples
* 4 + 12 )
325 >= GetMaxFileSize() )
326 { // 12 = sizeof chunk metadata
328 done
= !GetAutoSplit();
331 prevTimeCode
.sec
= timeCode
.sec
;
336 RawHandler
::RawHandler() : fd( -1 )
342 RawHandler
::~RawHandler()
348 bool RawHandler
::FileIsOpen()
354 bool RawHandler
::Create( const string
& filename
)
356 fd
= open( filename
.c_str(), O_CREAT
| O_TRUNC
| O_RDWR
| O_NONBLOCK
, 0644 );
359 FileTracker
::GetInstance().Add( filename
.c_str() );
360 this->filename
= filename
;
367 int RawHandler
::Write( const Frame
& frame
)
369 int result
= write( fd
, frame
.data
, frame
.GetFrameSize() );
374 int RawHandler
::Close()
385 off_t RawHandler
::GetFileSize()
387 struct stat file_status
;
388 fstat( fd
, &file_status
);
389 return file_status
.st_size
;
392 int RawHandler
::GetTotalFrames()
394 return GetFileSize() / ( 480 * numBlocks
);
398 bool RawHandler
::Open( const char *s
)
400 unsigned char data
[ 4 ];
402 fd
= open( s
, O_RDONLY
| O_NONBLOCK
);
405 if ( read( fd
, data
, 4 ) < 0 )
407 lseek( fd
, 0, SEEK_SET
);
408 numBlocks
= ( ( data
[ 3 ] & 0x80 ) == 0 ) ?
250 : 300;
414 int RawHandler
::GetFrame( uint8_t *data
, int frameNum
)
417 int size
= 480 * numBlocks
;
420 off_t offset
= ( ( off_t
) frameNum
* ( off_t
) size
);
421 fail_if( lseek( fd
, offset
, SEEK_SET
) == ( off_t
) - 1 );
422 if ( read( fd
, data
, size
) > 0 )
429 /***************************************************************************/
432 AVIHandler
::AVIHandler( int format
) : avi( NULL
), aviFormat( format
), isOpenDML( false ),
433 fccHandler( make_fourcc( "dvsd" ) ), channels( 2 ), isFullyInitialized( false ),
437 for ( int c
= 0; c
< 4; c
++ )
438 audioChannels
[ c
] = NULL
;
442 AVIHandler
::~AVIHandler()
444 if ( audioBuffer
!= NULL
)
449 for ( int c
= 0; c
< 4; c
++ )
451 if ( audioChannels
[ c
] != NULL
)
453 delete audioChannels
[ c
];
454 audioChannels
[ c
] = NULL
;
462 void AVIHandler
::SetSampleFrame( const Frame
& sample
)
465 sample
.GetAudioInfo( audioInfo
);
466 sample
.GetVideoInfo( videoInfo
);
468 sample
.GetAAUXPack( 0x50, pack
);
469 dvinfo
.dwDVAAuxSrc
= *( DWORD
* ) ( pack
.data
+ 1 );
470 sample
.GetAAUXPack( 0x51, pack
);
471 dvinfo
.dwDVAAuxCtl
= *( DWORD
* ) ( pack
.data
+ 1 );
473 sample
.GetAAUXPack( 0x52, pack
);
474 dvinfo
.dwDVAAuxSrc1
= *( DWORD
* ) ( pack
.data
+ 1 );
475 sample
.GetAAUXPack( 0x53, pack
);
476 dvinfo
.dwDVAAuxCtl1
= *( DWORD
* ) ( pack
.data
+ 1 );
478 sample
.GetVAUXPack( 0x60, pack
);
479 dvinfo
.dwDVVAuxSrc
= *( DWORD
* ) ( pack
.data
+ 1 );
480 sample
.GetVAUXPack( 0x61, pack
);
481 dvinfo
.dwDVVAuxCtl
= *( DWORD
* ) ( pack
.data
+ 1 );
485 if ( sample
.decoder
->std
== e_dv_std_smpte_314m
)
486 fccHandler
= make_fourcc( "dv25" );
491 bool AVIHandler
::FileIsOpen()
497 bool AVIHandler
::Create( const string
& filename
)
499 assert( avi
== NULL
);
505 fail_null( avi
= new AVI1File
);
506 if ( avi
->Create( filename
.c_str() ) == false )
508 //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency, AVI_LARGE_INDEX );
512 fail_null( avi
= new AVI2File
);
513 if ( avi
->Create( filename
.c_str() ) == false )
515 //if ( GetOpenDML() )
516 //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
517 //( AVI_SMALL_INDEX | AVI_LARGE_INDEX ) );
519 //avi->Init( videoInfo.isPAL ? AVI_PAL : AVI_NTSC, audioInfo.frequency,
520 //( AVI_SMALL_INDEX ) );
524 assert( aviFormat
== AVI_DV1_FORMAT
|| aviFormat
== AVI_DV2_FORMAT
);
527 avi
->setDVINFO( dvinfo
);
528 avi
->setFccHandler( make_fourcc( "iavs" ), fccHandler
);
529 avi
->setFccHandler( make_fourcc( "vids" ), fccHandler
);
530 this->filename
= filename
;
531 FileTracker
::GetInstance().Add( filename
.c_str() );
532 return ( avi
!= NULL
);
536 int AVIHandler
::Write( const Frame
& frame
)
538 assert( avi
!= NULL
);
541 return avi
->WriteFrame( frame
) ?
0 : -1;
550 int AVIHandler
::Close()
558 if ( audioBuffer
!= NULL
)
563 for ( int c
= 0; c
< 4; c
++ )
565 if ( audioChannels
[ c
] != NULL
)
567 delete audioChannels
[ c
];
568 audioChannels
[ c
] = NULL
;
571 isFullyInitialized
= false;
575 off_t AVIHandler
::GetFileSize()
577 return avi
->GetFileSize();
580 int AVIHandler
::GetTotalFrames()
582 return avi
->GetTotalFrames();
586 bool AVIHandler
::Open( const char *s
)
588 assert( avi
== NULL
);
589 fail_null( avi
= new AVI1File
);
590 if ( avi
->Open( s
) )
594 avi
->verifyStreamFormat( make_fourcc( "dvsd" ) ) ||
595 avi
->verifyStreamFormat( make_fourcc( "DVSD" ) ) ||
596 avi
->verifyStreamFormat( make_fourcc( "dvcs" ) ) ||
597 avi
->verifyStreamFormat( make_fourcc( "DVCS" ) ) ||
598 avi
->verifyStreamFormat( make_fourcc( "dvcp" ) ) ||
599 avi
->verifyStreamFormat( make_fourcc( "DVCP" ) ) ||
600 avi
->verifyStreamFormat( make_fourcc( "CDVC" ) ) ||
601 avi
->verifyStreamFormat( make_fourcc( "cdvc" ) ) ||
602 avi
->verifyStreamFormat( make_fourcc( "DV25" ) ) ||
603 avi
->verifyStreamFormat( make_fourcc( "dv25" ) ) ) )
606 if ( avi
->verifyStream( make_fourcc( "auds" ) ) )
607 aviFormat
= AVI_DV2_FORMAT
;
609 aviFormat
= AVI_DV1_FORMAT
;
610 isOpenDML
= avi
->isOpenDML();
619 int AVIHandler
::GetFrame( uint8_t *data
, int frameNum
)
621 int result
= avi
->GetDVFrame( data
, frameNum
);
625 /* get the audio from the audio stream, if available */
626 if ( aviFormat
== AVI_DV2_FORMAT
)
630 if ( ! isFullyInitialized
&&
631 avi
->getStreamFormat( ( void* ) &wav
, make_fourcc( "auds" ) ) )
633 if ( channels
> 0 && channels
< 5 )
635 // Allocate interleaved audio buffer
636 audioBuffer
= new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES
* channels
];
638 // Allocate non-interleaved audio buffers
639 for ( int c
= 0; c
< channels
; c
++ )
640 audioChannels
[ c
] = new int16_t[ 2 * DV_AUDIO_MAX_SAMPLES
];
642 // Get the audio parameters from AVI for subsequent calls to method
643 audioInfo
.channels
= wav
.nChannels
;
644 audioInfo
.frequency
= wav
.nSamplesPerSec
;
646 // Skip initialization on subsequent calls to method
647 isFullyInitialized
= true;
648 cerr
<< ">>> using audio from separate AVI audio stream" << endl
;
652 // Get the frame from AVI
653 int n
= avi
->getFrame( audioBuffer
, frameNum
, make_fourcc( "01wb" ) );
656 // Temporary pointer to audio scratch buffer
657 int16_t * s
= audioBuffer
;
659 // Determine samples in this frame
660 audioInfo
.samples
= n
/ audioInfo
.channels
/ sizeof( int16_t );
662 // Convert interleaved audio into non-interleaved
663 for ( int n
= 0; n
< audioInfo
.samples
; ++n
)
664 for ( int i
= 0; i
< audioInfo
.channels
; i
++ )
665 audioChannels
[ i
][ n
] = *s
++;
667 // Write interleaved audio into frame
668 frame
.EncodeAudio( audioInfo
, audioChannels
);
672 // Parse important metadata in DV bitstream
673 frame
.ExtractHeader();
680 void AVIHandler
::SetOpenDML( bool flag
)
686 bool AVIHandler
::GetOpenDML() const
692 /***************************************************************************/
694 #ifdef HAVE_LIBQUICKTIME
697 #define DV_AUDIO_MAX_SAMPLES 1944
700 // Missing fourcc's in libquicktime (allows compilation)
701 #ifndef QUICKTIME_DV_AVID
702 #define QUICKTIME_DV_AVID "AVdv"
705 #ifndef QUICKTIME_DV_AVID_A
706 #define QUICKTIME_DV_AVID_A "dvcp"
709 #ifndef QUICKTIME_DVCPRO
710 #define QUICKTIME_DVCPRO "dvpp"
713 QtHandler
::QtHandler() : fd( NULL
)
720 QtHandler
::~QtHandler()
725 void QtHandler
::Init()
732 samplesPerBuffer
= 0;
735 audioChannelBuffer
= NULL
;
736 isFullyInitialized
= false;
740 bool QtHandler
::FileIsOpen()
746 bool QtHandler
::Create( const string
& filename
)
750 if ( open( filename
.c_str(), O_CREAT
| O_TRUNC
| O_RDWR
| O_NONBLOCK
, 0644 ) != -1 )
752 fd
= quicktime_open( const_cast<char*>( filename
.c_str() ), 0, 1 );
754 FileTracker
::GetInstance().Add( filename
.c_str() );
758 this->filename
= filename
;
762 void QtHandler
::AllocateAudioBuffers()
764 if ( channels
> 0 && channels
< 5 )
766 audioBufferSize
= DV_AUDIO_MAX_SAMPLES
* 2;
767 audioBuffer
= new int16_t[ audioBufferSize
* channels
];
769 audioChannelBuffer
= new short int * [ channels
];
770 for ( int c
= 0; c
< channels
; c
++ )
771 audioChannelBuffer
[ c
] = new short int[ audioBufferSize
];
772 isFullyInitialized
= true;
776 inline void QtHandler
::DeinterlaceStereo16( void* pInput
, int iBytes
,
777 void* pLOutput
, void* pROutput
)
779 short int * piSampleInput
= ( short int* ) pInput
;
780 short int* piSampleLOutput
= ( short int* ) pLOutput
;
781 short int* piSampleROutput
= ( short int* ) pROutput
;
783 while ( ( char* ) piSampleInput
< ( ( char* ) pInput
+ iBytes
) )
785 *piSampleLOutput
++ = *piSampleInput
++;
786 *piSampleROutput
++ = *piSampleInput
++;
791 int QtHandler
::Write( const Frame
& frame
)
793 if ( ! isFullyInitialized
)
797 if ( frame
.GetAudioInfo( audio
) )
800 quicktime_set_audio( fd
, channels
, audio
.frequency
, 16, QUICKTIME_TWOS
);
807 quicktime_set_video( fd
, 1, 720, frame
.IsPAL() ?
576 : 480,
808 frame
.GetFrameRate(), QUICKTIME_DV
);
809 AllocateAudioBuffers();
812 int result
= quicktime_write_frame( fd
, const_cast<unsigned char*>( frame
.data
),
813 frame
.GetFrameSize(), 0 );
818 if ( frame
.GetAudioInfo( audio
) && ( unsigned int ) audio
.samples
< audioBufferSize
)
820 long bytesRead
= frame
.ExtractAudio( audioBuffer
);
822 DeinterlaceStereo16( audioBuffer
, bytesRead
,
823 audioChannelBuffer
[ 0 ],
824 audioChannelBuffer
[ 1 ] );
826 quicktime_encode_audio( fd
, audioChannelBuffer
, NULL
, audio
.samples
);
833 int QtHandler
::Close()
837 quicktime_close( fd
);
840 if ( audioBuffer
!= NULL
)
845 if ( audioChannelBuffer
!= NULL
)
847 for ( int c
= 0; c
< channels
; c
++ )
848 delete audioChannelBuffer
[ c
];
849 delete audioChannelBuffer
;
850 audioChannelBuffer
= NULL
;
856 off_t QtHandler
::GetFileSize()
858 struct stat file_status
;
859 stat( filename
.c_str(), &file_status
);
860 return file_status
.st_size
;
864 int QtHandler
::GetTotalFrames()
866 return ( int ) quicktime_video_length( fd
, 0 );
870 bool QtHandler
::Open( const char *s
)
874 fd
= quicktime_open( ( char * ) s
, 1, 0 );
877 fprintf( stderr
, "Error opening: %s\n", s
);
881 if ( quicktime_has_video( fd
) <= 0 )
883 fprintf( stderr
, "There must be at least one video track in the input file (%s).\n",
888 char * fcc
= quicktime_video_compressor( fd
, 0 );
889 if ( strncmp( fcc
, QUICKTIME_DV
, 4 ) != 0 &&
890 strncmp( fcc
, QUICKTIME_DV_AVID
, 4 ) != 0 &&
891 strncmp( fcc
, QUICKTIME_DV_AVID_A
, 4 ) != 0 &&
892 strncmp( fcc
, QUICKTIME_DVCPRO
, 4 ) != 0 )
897 if ( quicktime_has_audio( fd
) )
898 channels
= quicktime_track_channels( fd
, 0 );
903 int QtHandler
::GetFrame( uint8_t *data
, int frameNum
)
905 assert( fd
!= NULL
);
907 quicktime_set_video_position( fd
, frameNum
, 0 );
908 quicktime_read_frame( fd
, data
, 0 );
911 if ( quicktime_has_audio( fd
) )
913 if ( ! isFullyInitialized
)
914 AllocateAudioBuffers();
916 // Fetch the frequency of the audio track and calc number of samples needed
917 int frequency
= quicktime_sample_rate( fd
, 0 );
918 float fps
= ( data
[ 3 ] & 0x80 ) ?
25.0f
: 29.97f
;
919 int samples
= mlt_sample_calculator( fps
, frequency
, frameNum
);
920 int64_t seek
= mlt_sample_calculator_to_now( fps
, frequency
, frameNum
);
922 // Obtain a dv encoder and initialise it with minimal info
923 dv_encoder_t
*encoder
= dv_encoder_new( 0, 0, 0 );
924 encoder
->isPAL
= ( data
[ 3 ] & 0x80 );
925 encoder
->samples_this_frame
= samples
;
927 // Seek to the calculated position and decode
928 quicktime_set_audio_position( fd
, seek
, 0 );
929 lqt_decode_audio( fd
, audioChannelBuffer
, NULL
, (long) samples
);
931 // Encode the audio on the frame and done
932 dv_encode_full_audio( encoder
, audioChannelBuffer
, channels
, frequency
, data
);
933 dv_encoder_free( encoder
);