From: lilo_booter Date: Fri, 15 Apr 2005 14:28:26 +0000 (+0000) Subject: Initial version X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=815c458e95c73c39433fa7913afb4a830d7fd376;p=mlt Initial version git-svn-id: https://mlt.svn.sourceforge.net/svnroot/mlt/trunk/mlt@713 d19143bc-622f-0410-bfdd-b5b2a6649095 --- diff --git a/src/modules/kino/Makefile b/src/modules/kino/Makefile new file mode 100644 index 00000000..b595b914 --- /dev/null +++ b/src/modules/kino/Makefile @@ -0,0 +1,38 @@ +include ../../../config.mak +include config.mak + +TARGET=../libmltkino.so + +OBJS=factory.o producer_kino.o +CPPOBJS=kino_wrapper.o avi.o error.o filehandler.o riff.o +CFLAGS+=-I../../ +LDFLAGS+=-lmlt -lstdc++ +CXXFLAGS+=$(CFLAGS) -Wno-deprecated + +ifdef HAVE_LIBQUICKTIME +CXXFLAGS+=`lqt-config --cflags` +LDFLAGS+=`lqt-config --libs` +endif + +SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cc) + +all: $(TARGET) + +$(TARGET): $(OBJS) $(CPPOBJS) + $(CC) -shared -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend config.h config.mak + +clean: + rm -f $(OBJS) $(TARGET) $(CPPOBJS) + +install: all + install -m 755 $(TARGET) "$(prefix)/share/mlt/modules" + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/src/modules/kino/avi.cc b/src/modules/kino/avi.cc new file mode 100644 index 00000000..1ed29ad1 --- /dev/null +++ b/src/modules/kino/avi.cc @@ -0,0 +1,1817 @@ +/* +* avi.cc library for AVI file format i/o +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* 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. +* +* Tag: $Name$ +* +* Change log: +* +* $Log$ +* Revision 1.1 2005/04/15 14:28:26 lilo_booter +* Initial version +* +* Revision 1.28 2005/04/01 23:43:10 ddennedy +* apply endian fixes from Daniel Kobras +* +* Revision 1.27 2004/10/11 01:37:11 ddennedy +* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script +* +* Revision 1.26 2004/01/05 03:43:11 ddennedy +* metadata editing, deinterlace options, bugfixes and cleanups +* +* Revision 1.25 2003/11/25 23:00:52 ddennedy +* cleanup and a few bugfixes +* +* Revision 1.24 2003/11/12 13:01:56 ddennedy +* disable JUNK chunks in MOVI list, FileHandler max file size zero = infinity +* +* Revision 1.23 2003/11/10 01:02:51 ddennedy +* bugfix: return error on AVI directory entries with size <0 +* +* Revision 1.22 2003/10/28 18:52:32 ddennedy +* fix prefs dialog crash, improve WAV import +* +* Revision 1.21 2003/10/21 16:34:32 ddennedy +* GNOME2 port phase 1: initial checkin +* +* Revision 1.19.2.9 2003/08/26 20:39:00 ddennedy +* relocate mutex unlock and add assert includes +* +* Revision 1.19.2.8 2003/07/24 14:13:57 ddennedy +* support for distinct audio stream in type2 AVI and Quicktime; support for more DV FOURCCs +* +* Revision 1.19.2.7 2003/06/10 23:53:35 ddennedy +* Daniel Kobras' WriteFrame error handling and automatic OpenDML, bugfixes in scene list updates, export AV/C Record +* +* Revision 1.19.2.6 2003/03/05 15:02:12 ddennedy +* yet anther AV/C bugfix, yet another AVI improvement +* +* Revision 1.19.2.5 2003/02/20 21:59:55 ddennedy +* bugfixes to capture and AVI +* +* Revision 1.19.2.4 2003/01/13 05:15:31 ddennedy +* added More Info panel and supporting methods +* +* Revision 1.19.2.3 2002/12/31 22:40:49 ddennedy +* bugfix recent versions Quicktime4Linux build options, extend dvsd fourcc check on AVI to the BITMAPINFOHEADER for compatibility with mencoder +* +* Revision 1.19.2.2 2002/11/25 04:48:30 ddennedy +* bugfix to report errors when loading files +* +* Revision 1.19.2.1 2002/11/24 23:36:55 ddennedy +* bugfix in AVI writing +* +* Revision 1.19 2002/10/08 12:08:01 ddennedy +* more sane frame count, greater potential compatibility +* +* Revision 1.18 2002/10/08 08:33:02 ddennedy +* fix number of frames for small dv2 +* +* Revision 1.17 2002/10/08 07:46:41 ddennedy +* AVI bugfixes, compatibility, optimization, warn bad file in capture and export dv file, allow no mplex +* +* Revision 1.15 2002/06/10 10:39:51 ddennedy +* minor fixes for large files +* +* Revision 1.14 2002/05/17 08:04:24 ddennedy +* revert const-ness of Frame references in Frame, FileHandler, and AVI classes +* +* Revision 1.13 2002/05/15 04:39:35 ddennedy +* bugfixes to dv2 AVI write, audio export, Xv init +* +* Revision 1.12 2002/04/29 05:09:21 ddennedy +* raw dv file support, Frame::ExtractAudio uses libdv, audioScrub prefs +* +* Revision 1.11 2002/04/15 19:12:32 schirmacher +* removed debugging code causing performance losses and crashes with dv2 files +* +* Revision 1.10 2002/04/09 06:53:42 ddennedy +* cleanup, new libdv 0.9.5, large AVI, dnd storyboard +* +* Revision 1.8 2002/03/25 21:34:25 arne +* Support for large (64 bit) files mostly completed +* +* Revision 1.7 2002/03/10 21:28:29 arne +* release 1.1b1, 64 bit support for type 1 avis +* +* Revision 1.6 2002/03/10 13:29:41 arne +* more changes for 64 bit access +* +* Revision 1.5 2002/03/09 17:59:28 arne +* moved index routines to AVIFile +* +* Revision 1.4 2002/03/09 10:26:26 arne +* improved constructors and assignment operator +* +* Revision 1.3 2002/03/09 08:55:57 arne +* moved a few variables to AVIFile +* +* Revision 1.2 2002/03/04 19:22:43 arne +* updated to latest Kino avi code +* +* Revision 1.1.1.1 2002/03/03 19:08:08 arne +* import of version 1.01 +* +*/ + +#include "config.h" + +// C++ includes + +#include +#include +#include + +using std::cout; +using std::hex; +using std::dec; +using std::setw; +using std::setfill; +using std::endl; + +// C includes + +#include +#include +#include +#include + +// local includes + +#include "error.h" +#include "riff.h" +#include "avi.h" + +#define PADDING_SIZE (512) +#define PADDING_1GB (0x40000000) +#define IX00_INDEX_SIZE (4028) + +#define AVIF_HASINDEX 0x00000010 +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_TRUSTCKTYPE 0x00000800 +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_WASCAPTUREFILE 0x00010000 +#define AVIF_COPYRIGHTED 0x00020000 + + +//static char g_zeroes[ PADDING_SIZE ]; + +/** The constructor + + \todo mainHdr not initialized + \todo add checking for NULL pointers + +*/ + +AVIFile::AVIFile() : RIFFFile(), + idx1( NULL ), file_list( -1 ), riff_list( -1 ), + hdrl_list( -1 ), avih_chunk( -1 ), movi_list( -1 ), junk_chunk( -1 ), idx1_chunk( -1 ), + index_type( -1 ), current_ix00( -1 ), odml_list( -1 ), dmlh_chunk( -1 ), isUpdateIdx1( true ) +{ + // cerr << "0x" << hex << (long)this << dec << " AVIFile::AVIFile() : RIFFFile(), ..." << endl; + + for ( int i = 0; i < 2; ++i ) + { + indx[ i ] = new AVISuperIndex; + memset( indx[ i ], 0, sizeof( AVISuperIndex ) ); + ix[ i ] = new AVIStdIndex; + memset( ix[ i ], 0, sizeof( AVIStdIndex ) ); + indx_chunk[ i ] = -1; + ix_chunk[ i ] = -1; + strl_list[ i ] = -1; + strh_chunk[ i ] = -1; + strf_chunk[ i ] = -1; + } + idx1 = new AVISimpleIndex; + memset( idx1, 0, sizeof( AVISimpleIndex ) ); +} + + +/** The copy constructor + + \todo add checking for NULL pointers + +*/ + +AVIFile::AVIFile( const AVIFile& avi ) : RIFFFile( avi ) +{ + // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile::AVIFile(const AVIFile& avi) : RIFFFile(avi)" << endl; + + mainHdr = avi.mainHdr; + idx1 = new AVISimpleIndex; + *idx1 = *avi.idx1; + file_list = avi.file_list; + riff_list = avi.riff_list; + hdrl_list = avi.hdrl_list; + avih_chunk = avi.avih_chunk; + movi_list = avi.movi_list; + junk_chunk = avi.junk_chunk; + idx1_chunk = avi.idx1_chunk; + + for ( int i = 0; i < 2; ++i ) + { + indx[ i ] = new AVISuperIndex; + *indx[ i ] = *avi.indx[ i ]; + ix[ i ] = new AVIStdIndex; + *ix[ i ] = *avi.ix[ i ]; + indx_chunk[ i ] = avi.indx_chunk[ i ]; + ix_chunk[ i ] = avi.ix_chunk[ i ]; + strl_list[ i ] = avi.strl_list[ i ]; + strh_chunk[ i ] = avi.strh_chunk[ i ]; + strf_chunk[ i ] = avi.strf_chunk[ i ]; + } + + index_type = avi.index_type; + current_ix00 = avi.current_ix00; + + for ( int i = 0; i < 62; ++i ) + dmlh[ i ] = avi.dmlh[ i ]; + + isUpdateIdx1 = avi.isUpdateIdx1; + +} + + +/** The assignment operator + +*/ + +AVIFile& AVIFile::operator=( const AVIFile& avi ) +{ + // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile& AVIFile::operator=(const AVIFile& avi)" << endl; + + if ( this != &avi ) + { + RIFFFile::operator=( avi ); + mainHdr = avi.mainHdr; + *idx1 = *avi.idx1; + file_list = avi.file_list; + riff_list = avi.riff_list; + hdrl_list = avi.hdrl_list; + avih_chunk = avi.avih_chunk; + movi_list = avi.movi_list; + junk_chunk = avi.junk_chunk; + idx1_chunk = avi.idx1_chunk; + + for ( int i = 0; i < 2; ++i ) + { + *indx[ i ] = *avi.indx[ i ]; + *ix[ i ] = *avi.ix[ i ]; + indx_chunk[ i ] = avi.indx_chunk[ i ]; + ix_chunk[ i ] = avi.ix_chunk[ i ]; + strl_list[ i ] = avi.strl_list[ i ]; + strh_chunk[ i ] = avi.strh_chunk[ i ]; + strf_chunk[ i ] = avi.strf_chunk[ i ]; + } + + index_type = avi.index_type; + current_ix00 = avi.current_ix00; + + for ( int i = 0; i < 62; ++i ) + dmlh[ i ] = avi.dmlh[ i ]; + + isUpdateIdx1 = avi.isUpdateIdx1; + } + return *this; +} + + +/** The destructor + +*/ + +AVIFile::~AVIFile() +{ + // cerr << "0x" << hex << (long)this << dec << " AVIFile::~AVIFile()" << endl; + + for ( int i = 0; i < 2; ++i ) + { + delete ix[ i ]; + delete indx[ i ]; + } + delete idx1; +} + +/** Initialize the AVI structure to its initial state, either for PAL or NTSC format + + Initialize the AVIFile attributes: mainHdr, indx, ix00, idx1 + + \todo consolidate AVIFile::Init, AVI1File::Init, AVI2File::Init. They are somewhat redundant. + \param format pass AVI_PAL or AVI_NTSC + \param sampleFrequency the sample frequency of the audio content + \param indexType pass AVI_SMALL_INDEX or AVI_LARGE_INDEX + +*/ + +void AVIFile::Init( int format, int sampleFrequency, int indexType ) +{ + int i, j; + + assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) ); + + index_type = indexType; + + switch ( format ) + { + case AVI_PAL: + mainHdr.dwMicroSecPerFrame = 40000; + mainHdr.dwSuggestedBufferSize = 144008; + break; + + case AVI_NTSC: + mainHdr.dwMicroSecPerFrame = 33366; + mainHdr.dwSuggestedBufferSize = 120008; + break; + + default: /* no default allowed */ + assert( 0 ); + break; + } + + /* Initialize the 'avih' chunk */ + + mainHdr.dwMaxBytesPerSec = 3600000 + sampleFrequency * 4; + mainHdr.dwPaddingGranularity = PADDING_SIZE; + mainHdr.dwFlags = AVIF_TRUSTCKTYPE; + if ( indexType & AVI_SMALL_INDEX ) + mainHdr.dwFlags |= AVIF_HASINDEX; + mainHdr.dwTotalFrames = 0; + mainHdr.dwInitialFrames = 0; + mainHdr.dwStreams = 1; + mainHdr.dwWidth = 0; + mainHdr.dwHeight = 0; + mainHdr.dwReserved[ 0 ] = 0; + mainHdr.dwReserved[ 1 ] = 0; + mainHdr.dwReserved[ 2 ] = 0; + mainHdr.dwReserved[ 3 ] = 0; + + /* Initialize the 'idx1' chunk */ + + for ( int i = 0; i < 8000; ++i ) + { + idx1->aIndex[ i ].dwChunkId = 0; + idx1->aIndex[ i ].dwFlags = 0; + idx1->aIndex[ i ].dwOffset = 0; + idx1->aIndex[ i ].dwSize = 0; + } + idx1->nEntriesInUse = 0; + + /* Initialize the 'indx' chunk */ + + for ( i = 0; i < 2; ++i ) + { + indx[ i ] ->wLongsPerEntry = 4; + indx[ i ] ->bIndexSubType = 0; + indx[ i ] ->bIndexType = AVI_INDEX_OF_INDEXES; + indx[ i ] ->nEntriesInUse = 0; + indx[ i ] ->dwReserved[ 0 ] = 0; + indx[ i ] ->dwReserved[ 1 ] = 0; + indx[ i ] ->dwReserved[ 2 ] = 0; + for ( j = 0; j < 2014; ++j ) + { + indx[ i ] ->aIndex[ j ].qwOffset = 0; + indx[ i ] ->aIndex[ j ].dwSize = 0; + indx[ i ] ->aIndex[ j ].dwDuration = 0; + } + } + + /* The ix00 and ix01 chunk will be added dynamically in avi_write_frame + as needed */ + + /* Initialize the 'dmlh' chunk. I have no clue what this means + though */ + + for ( i = 0; i < 62; ++i ) + dmlh[ i ] = 0; + //dmlh[0] = -1; /* frame count + 1? */ + +} + + +/** Find position and size of a given frame in the file + + Depending on which index is available, search one of them to + find position and frame size + + \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr. + \todo all index related operations should be isolated + \param offset the file offset to the start of the frame + \param size the size of the frame + \param frameNum the number of the frame we wish to find + \return 0 if the frame could be found, -1 otherwise +*/ + +int AVIFile::GetDVFrameInfo( off_t &offset, int &size, int frameNum ) +{ + switch ( index_type ) + { + case AVI_LARGE_INDEX: + + /* find relevant index in indx0 */ + + int i; + + for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i ) + ; + + if ( i != current_ix00 ) + { + fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) ); + current_ix00 = i; + } + + if ( frameNum < ix[ 0 ] ->nEntriesInUse ) + { + offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset; + size = ix[ 0 ] ->aIndex[ frameNum ].dwSize; + return 0; + } + else + return -1; + break; + + case AVI_SMALL_INDEX: + int index = -1; + int frameNumIndex = 0; + for ( int i = 0; i < idx1->nEntriesInUse; ++i ) + { + FOURCC chunkID1 = make_fourcc( "00dc" ); + FOURCC chunkID2 = make_fourcc( "00db" ); + if ( idx1->aIndex[ i ].dwChunkId == chunkID1 || + idx1->aIndex[ i ].dwChunkId == chunkID2 ) + { + if ( frameNumIndex == frameNum ) + { + index = i; + break; + } + ++frameNumIndex; + } + } + if ( index != -1 ) + { + // compatibility check for broken dvgrab dv2 format + if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset ) + { + offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE; + } + else + { + // new, correct dv2 format + offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset; + } + size = idx1->aIndex[ index ].dwSize; + return 0; + } + else + return -1; + break; + } + return -1; +} + +/** Find position and size of a given frame in the file + + Depending on which index is available, search one of them to + find position and frame size + + \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr. + \todo all index related operations should be isolated + \param offset the file offset to the start of the frame + \param size the size of the frame + \param frameNum the number of the frame we wish to find + \param chunkID the ID of the type of chunk we want + \return 0 if the frame could be found, -1 otherwise +*/ + +int AVIFile::GetFrameInfo( off_t &offset, int &size, int frameNum, FOURCC chunkID ) +{ + if ( index_type & AVI_LARGE_INDEX ) + { + int i; + + for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i ) + ; + + if ( i != current_ix00 ) + { + fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) ); + current_ix00 = i; + } + + if ( frameNum < ix[ 0 ] ->nEntriesInUse ) + { + if ( ( FOURCC ) ix[ 0 ] ->dwChunkId == chunkID ) + { + offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset; + size = ix[ 0 ] ->aIndex[ frameNum ].dwSize; + return 0; + } + } + } + if ( index_type & AVI_SMALL_INDEX ) + { + int index = -1; + int frameNumIndex = 0; + for ( int i = 0; i < idx1->nEntriesInUse; ++i ) + { + if ( idx1->aIndex[ i ].dwChunkId == chunkID ) + { + if ( frameNumIndex == frameNum ) + { + index = i; + break; + } + ++frameNumIndex; + } + } + if ( index != -1 ) + { + // compatibility check for broken dvgrab dv2 format + if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset ) + { + offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE; + } + else + { + // new, correct dv2 format + offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset; + } + size = idx1->aIndex[ index ].dwSize; + return 0; + } + } + return -1; +} + +/** Read in a frame + + \todo we actually don't need the frame here, we could use just a void pointer + \param frame a reference to the frame object that will receive the frame data + \param frameNum the frame number to read + \return 0 if the frame could be read, -1 otherwise +*/ + +int AVIFile::GetDVFrame( uint8_t *data, int frameNum ) +{ + off_t offset; + int size; + + if ( GetDVFrameInfo( offset, size, frameNum ) != 0 || size < 0 ) + return -1; + pthread_mutex_lock( &file_mutex ); + fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, data, size ) ); + pthread_mutex_unlock( &file_mutex ); + + return 0; +} + +/** Read in a frame + + \param data a pointer to the audio buffer + \param frameNum the frame number to read + \param chunkID the ID of the type of chunk we want + \return the size the of the frame data, 0 if could not be read +*/ + +int AVIFile::getFrame( void *data, int frameNum, FOURCC chunkID ) +{ + off_t offset; + int size; + + if ( GetFrameInfo( offset, size, frameNum, chunkID ) != 0 ) + return 0; + fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, data, size ) ); + + return size; +} + +int AVIFile::GetTotalFrames() const +{ + return mainHdr.dwTotalFrames; +} + + +/** prints out a directory entry in text form + + Every subclass of RIFFFile is supposed to override this function + and to implement it for the entry types it knows about. For all + other entry types it should call its parent::PrintDirectoryData. + + \todo use 64 bit routines + \param entry the entry to print +*/ + +void AVIFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const +{ + static FOURCC lastStreamType = make_fourcc( " " ); + + if ( entry.type == make_fourcc( "avih" ) ) + { + + int i; + MainAVIHeader main_avi_header; + + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, &main_avi_header, sizeof( MainAVIHeader ) ) ); + + cout << " dwMicroSecPerFrame: " << ( int ) main_avi_header.dwMicroSecPerFrame << endl + << " dwMaxBytesPerSec: " << ( int ) main_avi_header.dwMaxBytesPerSec << endl + << " dwPaddingGranularity: " << ( int ) main_avi_header.dwPaddingGranularity << endl + << " dwFlags: " << ( int ) main_avi_header.dwFlags << endl + << " dwTotalFrames: " << ( int ) main_avi_header.dwTotalFrames << endl + << " dwInitialFrames: " << ( int ) main_avi_header.dwInitialFrames << endl + << " dwStreams: " << ( int ) main_avi_header.dwStreams << endl + << " dwSuggestedBufferSize: " << ( int ) main_avi_header.dwSuggestedBufferSize << endl + << " dwWidth: " << ( int ) main_avi_header.dwWidth << endl + << " dwHeight: " << ( int ) main_avi_header.dwHeight << endl; + for ( i = 0; i < 4; ++i ) + cout << " dwReserved[" << i << "]: " << ( int ) main_avi_header.dwReserved[ i ] << endl; + + } + else if ( entry.type == make_fourcc( "strh" ) ) + { + + AVIStreamHeader avi_stream_header; + + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, &avi_stream_header, sizeof( AVIStreamHeader ) ) ); + + lastStreamType = avi_stream_header.fccType; + + cout << " fccType: '" + << ((char *)&avi_stream_header.fccType)[0] + << ((char *)&avi_stream_header.fccType)[1] + << ((char *)&avi_stream_header.fccType)[2] + << ((char *)&avi_stream_header.fccType)[3] + << '\'' << endl + << " fccHandler: '" + << ((char *)&avi_stream_header.fccHandler)[0] + << ((char *)&avi_stream_header.fccHandler)[1] + << ((char *)&avi_stream_header.fccHandler)[2] + << ((char *)&avi_stream_header.fccHandler)[3] + << '\'' << endl + << " dwFlags: " << ( int ) avi_stream_header.dwFlags << endl + << " wPriority: " << ( int ) avi_stream_header.wPriority << endl + << " wLanguage: " << ( int ) avi_stream_header.wLanguage << endl + << " dwInitialFrames: " << ( int ) avi_stream_header.dwInitialFrames << endl + << " dwScale: " << ( int ) avi_stream_header.dwScale << endl + << " dwRate: " << ( int ) avi_stream_header.dwRate << endl + << " dwLength: " << ( int ) avi_stream_header.dwLength << endl + << " dwQuality: " << ( int ) avi_stream_header.dwQuality << endl + << " dwSampleSize: " << ( int ) avi_stream_header.dwSampleSize << endl; + + } + else if ( entry.type == make_fourcc( "indx" ) ) + { + + int i; + AVISuperIndex avi_super_index; + + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, &avi_super_index, sizeof( AVISuperIndex ) ) ); + + cout << " wLongsPerEntry: " << ( int ) avi_super_index.wLongsPerEntry + << endl + << " bIndexSubType: " << ( int ) avi_super_index.bIndexSubType << endl + << " bIndexType: " << ( int ) avi_super_index.bIndexType << endl + << " nEntriesInUse: " << ( int ) avi_super_index.nEntriesInUse << endl + << " dwChunkId: '" + << ((char *)&avi_super_index.dwChunkId)[0] + << ((char *)&avi_super_index.dwChunkId)[1] + << ((char *)&avi_super_index.dwChunkId)[2] + << ((char *)&avi_super_index.dwChunkId)[3] + << '\'' << endl + << " dwReserved[0]: " << ( int ) avi_super_index.dwReserved[ 0 ] << endl + << " dwReserved[1]: " << ( int ) avi_super_index.dwReserved[ 1 ] << endl + << " dwReserved[2]: " << ( int ) avi_super_index.dwReserved[ 2 ] << endl; + for ( i = 0; i < avi_super_index.nEntriesInUse; ++i ) + { + cout << ' ' << setw( 4 ) << setfill( ' ' ) << i + << ": qwOffset : 0x" << setw( 12 ) << setfill( '0' ) << hex << avi_super_index.aIndex[ i ].qwOffset << endl + << " dwSize : 0x" << setw( 8 ) << avi_super_index.aIndex[ i ].dwSize << endl + << " dwDuration : " << dec << avi_super_index.aIndex[ i ].dwDuration << endl; + } + } + else if ( entry.type == make_fourcc( "strf" ) ) + { + if ( lastStreamType == make_fourcc( "auds" ) ) + { + WAVEFORMATEX waveformatex; + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, &waveformatex, sizeof( WAVEFORMATEX ) ) ); + cout << " waveformatex.wFormatTag : " << waveformatex.wFormatTag << endl; + cout << " waveformatex.nChannels : " << waveformatex.nChannels << endl; + cout << " waveformatex.nSamplesPerSec : " << waveformatex.nSamplesPerSec << endl; + cout << " waveformatex.nAvgBytesPerSec: " << waveformatex.nAvgBytesPerSec << endl; + cout << " waveformatex.nBlockAlign : " << waveformatex.nBlockAlign << endl; + cout << " waveformatex.wBitsPerSample : " << waveformatex.wBitsPerSample << endl; + cout << " waveformatex.cbSize : " << waveformatex.cbSize << endl; + } + else if ( lastStreamType == make_fourcc( "vids" ) ) + { + BITMAPINFOHEADER bitmapinfo; + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, &bitmapinfo, sizeof( BITMAPINFOHEADER ) ) ); + cout << " bitmapinfo.biSize : " << bitmapinfo.biSize << endl; + cout << " bitmapinfo.biWidth : " << bitmapinfo.biWidth << endl; + cout << " bitmapinfo.biHeight : " << bitmapinfo.biHeight << endl; + cout << " bitmapinfo.biPlanes : " << bitmapinfo.biPlanes << endl; + cout << " bitmapinfo.biBitCount : " << bitmapinfo.biBitCount << endl; + cout << " bitmapinfo.biCompression : " << bitmapinfo.biCompression << endl; + cout << " bitmapinfo.biSizeImage : " << bitmapinfo.biSizeImage << endl; + cout << " bitmapinfo.biXPelsPerMeter: " << bitmapinfo.biXPelsPerMeter << endl; + cout << " bitmapinfo.biYPelsPerMeter: " << bitmapinfo.biYPelsPerMeter << endl; + cout << " bitmapinfo.biClrUsed : " << bitmapinfo.biClrUsed << endl; + cout << " bitmapinfo.biClrImportant : " << bitmapinfo.biClrImportant << endl; + } + else if ( lastStreamType == make_fourcc( "iavs" ) ) + { + DVINFO dvinfo; + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, &dvinfo, sizeof( DVINFO ) ) ); + cout << " dvinfo.dwDVAAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc << endl; + cout << " dvinfo.dwDVAAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl << endl; + cout << " dvinfo.dwDVAAuxSrc1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc1 << endl; + cout << " dvinfo.dwDVAAuxCtl1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl1 << endl; + cout << " dvinfo.dwDVVAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxSrc << endl; + cout << " dvinfo.dwDVVAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxCtl << endl; + } + } + + /* This is the Standard Index. It is an array of offsets and + sizes relative to some start offset. */ + + else if ( ( entry.type == make_fourcc( "ix00" ) ) || ( entry.type == make_fourcc( "ix01" ) ) ) + { + + int i; + AVIStdIndex avi_std_index; + + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, &avi_std_index, sizeof( AVIStdIndex ) ) ); + + cout << " wLongsPerEntry: " << ( int ) avi_std_index.wLongsPerEntry + << endl + << " bIndexSubType: " << ( int ) avi_std_index.bIndexSubType << endl + << " bIndexType: " << ( int ) avi_std_index.bIndexType << endl + << " nEntriesInUse: " << ( int ) avi_std_index.nEntriesInUse << endl + << " dwChunkId: '" + << ((char *)&avi_std_index.dwChunkId)[0] + << ((char *)&avi_std_index.dwChunkId)[1] + << ((char *)&avi_std_index.dwChunkId)[2] + << ((char *)&avi_std_index.dwChunkId)[3] + << '\'' << endl + << " qwBaseOffset: 0x" << setw( 12 ) << hex << avi_std_index.qwBaseOffset << endl + << " dwReserved: " << dec << ( int ) avi_std_index.dwReserved << endl; + for ( i = 0; i < avi_std_index.nEntriesInUse; ++i ) + { + cout << ' ' << setw( 4 ) << setfill( ' ' ) << i + << ": dwOffset : 0x" << setw( 8 ) << setfill( '0' ) << hex << avi_std_index.aIndex[ i ].dwOffset + << " (0x" << setw( 12 ) << avi_std_index.qwBaseOffset + avi_std_index.aIndex[ i ].dwOffset << ')' << endl + << " dwSize : 0x" << setw( 8 ) << avi_std_index.aIndex[ i ].dwSize << dec << endl; + } + + } + else if ( entry.type == make_fourcc( "idx1" ) ) + { + + int i; + int numEntries = entry.length / sizeof( int ) / 4; + DWORD *idx1 = new DWORD[ numEntries * 4 ]; + // FOURCC movi_list = FindDirectoryEntry(make_fourcc("movi")); + + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, idx1, entry.length ) ); + + for ( i = 0; i < numEntries; ++i ) + { + + cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": dwChunkId : '" + << ((char *)&idx1[ i * 4 + 0 ])[0] + << ((char *)&idx1[ i * 4 + 0 ])[1] + << ((char *)&idx1[ i * 4 + 0 ])[2] + << ((char *)&idx1[ i * 4 + 0 ])[3] + << '\'' << endl + << " dwType : 0x" << setw( 8 ) << hex << idx1[ i * 4 + 1 ] << endl + << " dwOffset : 0x" << setw( 8 ) << idx1[ i * 4 + 2 ] << endl + // << " (0x" << setw(8) << idx1[i * 4 + 2] + GetDirectoryEntry(movi_list).offset << ')' << endl + << " dwSize : 0x" << setw( 8 ) << idx1[ i * 4 + 3 ] << dec << endl; + } + + delete[] idx1; + } + else if ( entry.type == make_fourcc( "dmlh" ) ) + { + int i; + int numEntries = entry.length / sizeof( int ); + DWORD *dmlh = new DWORD[ numEntries ]; + + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, dmlh, entry.length ) ); + + for ( i = 0; i < numEntries; ++i ) + { + cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": " + << " dwTotalFrames: 0x" << setw( 8 ) << hex << dmlh[ i ] + << " (" << dec << dmlh[ i ] << ")" << endl; + } + delete[] dmlh; + } +} + + +/** If this is not a movi list, read its contents + +*/ + +void AVIFile::ParseList( int parent ) +{ + FOURCC type; + FOURCC name; + DWORD length; + int list; + off_t pos; + off_t listEnd; + + /* Read in the chunk header (type and length). */ + fail_neg( read( fd, &type, sizeof( type ) ) ); + fail_neg( read( fd, &length, sizeof( length ) ) ); + if ( length & 1 ) + length++; + + /* The contents of the list starts here. Obtain its offset. The list + name (4 bytes) is already part of the contents). */ + + pos = lseek( fd, 0, SEEK_CUR ); + fail_if( pos == ( off_t ) - 1 ); + fail_neg( read( fd, &name, sizeof( name ) ) ); + + /* if we encounter a movi list, do not read it. It takes too much time + and we don't need it anyway. */ + + if ( name != make_fourcc( "movi" ) ) + { + // if (1) { + + /* Add an entry for this list. */ + list = AddDirectoryEntry( type, name, sizeof( name ), parent ); + + /* Read in any chunks contained in this list. This list is the + parent for all chunks it contains. */ + + listEnd = pos + length; + while ( pos < listEnd ) + { + ParseChunk( list ); + pos = lseek( fd, 0, SEEK_CUR ); + fail_if( pos == ( off_t ) - 1 ); + } + } + else + { + /* Add an entry for this list. */ + + movi_list = AddDirectoryEntry( type, name, length, parent ); + + pos = lseek( fd, length - 4, SEEK_CUR ); + fail_if( pos == ( off_t ) - 1 ); + } +} + + +void AVIFile::ParseRIFF() +{ + RIFFFile::ParseRIFF(); + + avih_chunk = FindDirectoryEntry( make_fourcc( "avih" ) ); + if ( avih_chunk != -1 ) + ReadChunk( avih_chunk, ( void* ) & mainHdr ); +} + + +void AVIFile::ReadIndex() +{ + indx_chunk[ 0 ] = FindDirectoryEntry( make_fourcc( "indx" ) ); + if ( indx_chunk[ 0 ] != -1 ) + { + ReadChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] ); + index_type = AVI_LARGE_INDEX; + + /* recalc number of frames from each index */ + mainHdr.dwTotalFrames = 0; + for ( int i = 0; + i < indx[ 0 ] ->nEntriesInUse; + mainHdr.dwTotalFrames += indx[ 0 ] ->aIndex[ i++ ].dwDuration ) + ; + return ; + } + idx1_chunk = FindDirectoryEntry( make_fourcc( "idx1" ) ); + if ( idx1_chunk != -1 ) + { + ReadChunk( idx1_chunk, ( void* ) idx1 ); + idx1->nEntriesInUse = GetDirectoryEntry( idx1_chunk ).length / 16; + index_type = AVI_SMALL_INDEX; + + /* recalc number of frames from the simple index */ + int frameNumIndex = 0; + FOURCC chunkID1 = make_fourcc( "00dc" ); + FOURCC chunkID2 = make_fourcc( "00db" ); + for ( int i = 0; i < idx1->nEntriesInUse; ++i ) + { + if ( idx1->aIndex[ i ].dwChunkId == chunkID1 || + idx1->aIndex[ i ].dwChunkId == chunkID2 ) + { + ++frameNumIndex; + } + } + mainHdr.dwTotalFrames = frameNumIndex; + return ; + } +} + + +void AVIFile::FlushIndx( int stream ) +{ + FOURCC type; + FOURCC name; + off_t length; + off_t offset; + int parent; + int i; + + /* Write out the previous index. When this function is + entered for the first time, there is no index to + write. Note: this may be an expensive operation + because of a time consuming seek to the former file + position. */ + + if ( ix_chunk[ stream ] != -1 ) + WriteChunk( ix_chunk[ stream ], ix[ stream ] ); + + /* make a new ix chunk. */ + + if ( stream == 0 ) + type = make_fourcc( "ix00" ); + else + type = make_fourcc( "ix01" ); + ix_chunk[ stream ] = AddDirectoryEntry( type, 0, sizeof( AVIStdIndex ), movi_list ); + GetDirectoryEntry( ix_chunk[ stream ], type, name, length, offset, parent ); + + /* fill out all required fields. The offsets in the + array are relative to qwBaseOffset, so fill in the + offset to the next free location in the file + there. */ + + ix[ stream ] ->wLongsPerEntry = 2; + ix[ stream ] ->bIndexSubType = 0; + ix[ stream ] ->bIndexType = AVI_INDEX_OF_CHUNKS; + ix[ stream ] ->nEntriesInUse = 0; + ix[ stream ] ->dwChunkId = indx[ stream ] ->dwChunkId; + ix[ stream ] ->qwBaseOffset = offset + length; + ix[ stream ] ->dwReserved = 0; + + for ( i = 0; i < IX00_INDEX_SIZE; ++i ) + { + ix[ stream ] ->aIndex[ i ].dwOffset = 0; + ix[ stream ] ->aIndex[ i ].dwSize = 0; + } + + /* add a reference to this new index in our super + index. */ + + i = indx[ stream ] ->nEntriesInUse++; + indx[ stream ] ->aIndex[ i ].qwOffset = offset - RIFF_HEADERSIZE; + indx[ stream ] ->aIndex[ i ].dwSize = length + RIFF_HEADERSIZE; + indx[ stream ] ->aIndex[ i ].dwDuration = 0; +} + + +void AVIFile::UpdateIndx( int stream, int chunk, int duration ) +{ + FOURCC type; + FOURCC name; + off_t length; + off_t offset; + int parent; + int i; + + /* update the appropiate entry in the super index. It reflects + the number of frames in the referenced index. */ + + i = indx[ stream ] ->nEntriesInUse - 1; + indx[ stream ] ->aIndex[ i ].dwDuration += duration; + + /* update the standard index. Calculate the file position of + the new frame. */ + + GetDirectoryEntry( chunk, type, name, length, offset, parent ); + + indx[ stream ] ->dwChunkId = type; + i = ix[ stream ] ->nEntriesInUse++; + ix[ stream ] ->aIndex[ i ].dwOffset = offset - ix[ stream ] ->qwBaseOffset; + ix[ stream ] ->aIndex[ i ].dwSize = length; +} + + +void AVIFile::UpdateIdx1( int chunk, int flags ) +{ + if ( idx1->nEntriesInUse < 20000 ) + { + FOURCC type; + FOURCC name; + off_t length; + off_t offset; + int parent; + + GetDirectoryEntry( chunk, type, name, length, offset, parent ); + + idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = type; + idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = flags; + idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = offset - GetDirectoryEntry( movi_list ).offset - RIFF_HEADERSIZE; + idx1->aIndex[ idx1->nEntriesInUse ].dwSize = length; + idx1->nEntriesInUse++; + } +} + +bool AVIFile::verifyStreamFormat( FOURCC type ) +{ + int i, j = 0; + AVIStreamHeader avi_stream_header; + BITMAPINFOHEADER bih; + FOURCC strh = make_fourcc( "strh" ); + FOURCC strf = make_fourcc( "strf" ); + + while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 ) + { + ReadChunk( i, ( void* ) & avi_stream_header ); + if ( avi_stream_header.fccHandler == type ) + return true; + } + j = 0; + while ( ( i = FindDirectoryEntry( strf, j++ ) ) != -1 ) + { + ReadChunk( i, ( void* ) & bih ); + if ( ( FOURCC ) bih.biCompression == type ) + return true; + } + + return false; +} + +bool AVIFile::verifyStream( FOURCC type ) +{ + int i, j = 0; + AVIStreamHeader avi_stream_header; + FOURCC strh = make_fourcc( "strh" ); + + while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 ) + { + ReadChunk( i, ( void* ) & avi_stream_header ); + if ( avi_stream_header.fccType == type ) + return true; + } + return false; +} + +bool AVIFile::isOpenDML( void ) +{ + int i, j = 0; + FOURCC dmlh = make_fourcc( "dmlh" ); + + while ( ( i = FindDirectoryEntry( dmlh, j++ ) ) != -1 ) + { + return true; + } + return false; +} + +AVI1File::AVI1File() : AVIFile() +{} + + +AVI1File::~AVI1File() +{} + + +/* Initialize the AVI structure to its initial state, either for PAL + or NTSC format */ + +void AVI1File::Init( int format, int sampleFrequency, int indexType ) +{ + int num_blocks; + FOURCC type; + FOURCC name; + off_t length; + off_t offset; + int parent; + + assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) ); + + AVIFile::Init( format, sampleFrequency, indexType ); + + switch ( format ) + { + case AVI_PAL: + mainHdr.dwWidth = 720; + mainHdr.dwHeight = 576; + + streamHdr[ 0 ].dwScale = 1; + streamHdr[ 0 ].dwRate = 25; + streamHdr[ 0 ].dwSuggestedBufferSize = 144008; + + /* initialize the 'strf' chunk */ + + /* Meaning of the DV stream format chunk per Microsoft + dwDVAAuxSrc + Specifies the Audio Auxiliary Data Source Pack for the first audio block + (first 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of + a frame. A DIF sequence is a data block that contains 150 DIF blocks. A DIF block consists + of 80 bytes. The Audio Auxiliary Data Source Pack is defined in section D.7.1 of Part 2, + Annex D, "The Pack Header Table and Contents of Packs" of the Specification of + Consumer-use Digital VCRs. + dwDVAAuxCtl + Specifies the Audio Auxiliary Data Source Control Pack for the first audio block of a + frame. The Audio Auxiliary Data Control Pack is defined in section D.7.2 of Part 2, + Annex D, "The Pack Header Table and Contents of Packs" of the Specification of + Consumer-use Digital VCRs. + dwDVAAuxSrc1 + Specifies the Audio Auxiliary Data Source Pack for the second audio block + (second 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of a frame. + dwDVAAuxCtl1 + Specifies the Audio Auxiliary Data Source Control Pack for the second audio block of a frame. + dwDVVAuxSrc + Specifies the Video Auxiliary Data Source Pack as defined in section D.8.1 of Part 2, + Annex D, "The Pack Header Table and Contents of Packs" of the Specification of + Consumer-use Digital VCRs. + dwDVVAuxCtl + Specifies the Video Auxiliary Data Source Control Pack as defined in section D.8.2 of Part 2, + Annex D, "The Pack Header Table and Contents of Packs" of the Specification of + Consumer-use Digital VCRs. + dwDVReserved[2] + Reserved. Set this array to zero. + */ + + dvinfo.dwDVAAuxSrc = 0xd1e030d0; + dvinfo.dwDVAAuxCtl = 0xffa0cf3f; + dvinfo.dwDVAAuxSrc1 = 0xd1e03fd0; + dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f; + dvinfo.dwDVVAuxSrc = 0xff20ffff; + dvinfo.dwDVVAuxCtl = 0xfffdc83f; + dvinfo.dwDVReserved[ 0 ] = 0; + dvinfo.dwDVReserved[ 1 ] = 0; + break; + + case AVI_NTSC: + mainHdr.dwWidth = 720; + mainHdr.dwHeight = 480; + + streamHdr[ 0 ].dwScale = 1001; + streamHdr[ 0 ].dwRate = 30000; + streamHdr[ 0 ].dwSuggestedBufferSize = 120008; + + /* initialize the 'strf' chunk */ + dvinfo.dwDVAAuxSrc = 0xc0c000c0; + dvinfo.dwDVAAuxCtl = 0xffa0cf3f; + dvinfo.dwDVAAuxSrc1 = 0xc0c001c0; + dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f; + dvinfo.dwDVVAuxSrc = 0xff80ffff; + dvinfo.dwDVVAuxCtl = 0xfffcc83f; + dvinfo.dwDVReserved[ 0 ] = 0; + dvinfo.dwDVReserved[ 1 ] = 0; + break; + + default: /* no default allowed */ + assert( 0 ); + break; + } + + indx[ 0 ] ->dwChunkId = make_fourcc( "00__" ); + + /* Initialize the 'strh' chunk */ + + streamHdr[ 0 ].fccType = make_fourcc( "iavs" ); + streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" ); + streamHdr[ 0 ].dwFlags = 0; + streamHdr[ 0 ].wPriority = 0; + streamHdr[ 0 ].wLanguage = 0; + streamHdr[ 0 ].dwInitialFrames = 0; + streamHdr[ 0 ].dwStart = 0; + streamHdr[ 0 ].dwLength = 0; + streamHdr[ 0 ].dwQuality = 0; + streamHdr[ 0 ].dwSampleSize = 0; + streamHdr[ 0 ].rcFrame.top = 0; + streamHdr[ 0 ].rcFrame.bottom = 0; + streamHdr[ 0 ].rcFrame.left = 0; + streamHdr[ 0 ].rcFrame.right = 0; + + /* This is a simple directory structure setup. For details see the + "OpenDML AVI File Format Extensions" document. + + An AVI file contains basically two types of objects, a + "chunk" and a "list" object. The list object contains any + number of chunks. Since a list is also a chunk, it is + possible to create a hierarchical "list of lists" + structure. + + Every AVI file starts with a "RIFF" object, which is a list + of several other required objects. The actual DV data is + contained in a "movi" list, each frame is in its own chunk. + + Old AVI files (pre OpenDML V. 1.02) contain only one RIFF + chunk of less than 1 GByte size per file. The current + format which allow for almost arbitrary sizes can contain + several RIFF chunks of less than 1 GByte size. Old software + however would only deal with the first RIFF chunk. + + Note that the first entry (FILE) isn´t actually part + of the AVI file. I use this (pseudo-) directory entry to + keep track of the RIFF chunks and their positions in the + AVI file. + */ + + /* Create the container directory entry */ + + file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT ); + + /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */ + + riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list ); + hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list ); + avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list ); + strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list ); + strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] ); + strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( dvinfo ), strl_list[ 0 ] ); + if ( index_type & AVI_LARGE_INDEX ) + indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] ); + + odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list ); + dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list ); + + /* align movi list to block */ + GetDirectoryEntry( hdrl_list, type, name, length, offset, parent ); + num_blocks = length / PADDING_SIZE + 1; + length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5? + junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list ); + + movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list ); + + /* The ix00 chunk will be added dynamically to the movi_list in avi_write_frame + as needed */ + + ix_chunk[ 0 ] = -1; +} + + +/* Write a DV video frame. This is somewhat complex... */ + +#if 0 +bool AVI1File::WriteFrame( const Frame &frame ) +{ + int frame_chunk; + int junk_chunk; + int num_blocks; + FOURCC type; + FOURCC name; + off_t length; + off_t offset; + int parent; + + /* exit if no large index and 1GB reached */ + if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false ) + return false; + + /* Check if we need a new ix00 Standard Index. It has a + capacity of IX00_INDEX_SIZE frames. Whenever we exceed that + number, we need a new index. The new ix00 chunk is also + part of the movi list. */ + + if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) ) + FlushIndx( 0 ); + + /* Write the DV frame data. + + Make a new 00__ chunk for the new frame, write out the + frame. */ + + frame_chunk = AddDirectoryEntry( make_fourcc( "00__" ), 0, frame.GetFrameSize(), movi_list ); + if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 ) + { + GetDirectoryEntry( frame_chunk, type, name, length, offset, parent ); + ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE; + } + WriteChunk( frame_chunk, frame.data ); + // num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1; + // length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE; + // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list); + // WriteChunk(junk_chunk, g_zeroes); + + if ( index_type & AVI_LARGE_INDEX ) + UpdateIndx( 0, frame_chunk, 1 ); + if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 ) + UpdateIdx1( frame_chunk, 0x10 ); + + /* update some variables with the new frame count. */ + + if ( isUpdateIdx1 ) + ++mainHdr.dwTotalFrames; + ++streamHdr[ 0 ].dwLength; + ++dmlh[ 0 ]; + + /* Find out if the current riff list is close to 1 GByte in + size. If so, start a new (extended) RIFF. The only allowed + item in the new RIFF chunk is a movi list (with video + frames and indexes as usual). */ + + GetDirectoryEntry( riff_list, type, name, length, offset, parent ); + if ( length > 0x3f000000 ) + { + /* write idx1 only once and before end of first GB */ + if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 ) + { + int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list ); + WriteChunk( idx1_chunk, ( void* ) idx1 ); + } + isUpdateIdx1 = false; + + if ( index_type & AVI_LARGE_INDEX ) + { + /* pad out to 1GB */ + //GetDirectoryEntry(riff_list, type, name, length, offset, parent); + //junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, PADDING_1GB - length - 5 * RIFF_HEADERSIZE, riff_list); + //WriteChunk(junk_chunk, g_zeroes); + + /* padding for alignment */ + GetDirectoryEntry( riff_list, type, name, length, offset, parent ); + num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1; + length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE; + if ( length > 0 ) + { + junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list ); + WriteChunk( junk_chunk, g_zeroes ); + } + + riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list ); + movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list ); + } + } + return true; +} +#endif + +void AVI1File::WriteRIFF() +{ + + WriteChunk( avih_chunk, ( void* ) & mainHdr ); + WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] ); + WriteChunk( strf_chunk[ 0 ], ( void* ) & dvinfo ); + WriteChunk( dmlh_chunk, ( void* ) & dmlh ); + + if ( index_type & AVI_LARGE_INDEX ) + { + WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] ); + WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] ); + } + + if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 ) + { + int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list ); + WriteChunk( idx1_chunk, ( void* ) idx1 ); + } + + RIFFFile::WriteRIFF(); +} + + +AVI2File::AVI2File() : AVIFile() +{} + + +AVI2File::~AVI2File() +{} + + +/* Initialize the AVI structure to its initial state, either for PAL + or NTSC format */ + +void AVI2File::Init( int format, int sampleFrequency, int indexType ) +{ + int num_blocks; + FOURCC type; + FOURCC name; + off_t length; + off_t offset; + int parent; + + assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) ); + + AVIFile::Init( format, sampleFrequency, indexType ); + + switch ( format ) + { + + case AVI_PAL: + mainHdr.dwStreams = 2; + mainHdr.dwWidth = 720; + mainHdr.dwHeight = 576; + + /* Initialize the 'strh' chunk */ + + streamHdr[ 0 ].fccType = make_fourcc( "vids" ); + streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" ); + streamHdr[ 0 ].dwFlags = 0; + streamHdr[ 0 ].wPriority = 0; + streamHdr[ 0 ].wLanguage = 0; + streamHdr[ 0 ].dwInitialFrames = 0; + streamHdr[ 0 ].dwScale = 1; + streamHdr[ 0 ].dwRate = 25; + streamHdr[ 0 ].dwStart = 0; + streamHdr[ 0 ].dwLength = 0; + streamHdr[ 0 ].dwSuggestedBufferSize = 144008; + streamHdr[ 0 ].dwQuality = -1; + streamHdr[ 0 ].dwSampleSize = 0; + streamHdr[ 0 ].rcFrame.top = 0; + streamHdr[ 0 ].rcFrame.bottom = 0; + streamHdr[ 0 ].rcFrame.left = 0; + streamHdr[ 0 ].rcFrame.right = 0; + + bitmapinfo.biSize = sizeof( bitmapinfo ); + bitmapinfo.biWidth = 720; + bitmapinfo.biHeight = 576; + bitmapinfo.biPlanes = 1; + bitmapinfo.biBitCount = 24; + bitmapinfo.biCompression = make_fourcc( "dvsd" ); + bitmapinfo.biSizeImage = 144000; + bitmapinfo.biXPelsPerMeter = 0; + bitmapinfo.biYPelsPerMeter = 0; + bitmapinfo.biClrUsed = 0; + bitmapinfo.biClrImportant = 0; + + streamHdr[ 1 ].fccType = make_fourcc( "auds" ); + streamHdr[ 1 ].fccHandler = 0; + streamHdr[ 1 ].dwFlags = 0; + streamHdr[ 1 ].wPriority = 0; + streamHdr[ 1 ].wLanguage = 0; + streamHdr[ 1 ].dwInitialFrames = 0; + streamHdr[ 1 ].dwScale = 2 * 2; + streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2; + streamHdr[ 1 ].dwStart = 0; + streamHdr[ 1 ].dwLength = 0; + streamHdr[ 1 ].dwSuggestedBufferSize = 8192; + streamHdr[ 1 ].dwQuality = -1; + streamHdr[ 1 ].dwSampleSize = 2 * 2; + streamHdr[ 1 ].rcFrame.top = 0; + streamHdr[ 1 ].rcFrame.bottom = 0; + streamHdr[ 1 ].rcFrame.left = 0; + streamHdr[ 1 ].rcFrame.right = 0; + + break; + + case AVI_NTSC: + mainHdr.dwTotalFrames = 0; + mainHdr.dwStreams = 2; + mainHdr.dwWidth = 720; + mainHdr.dwHeight = 480; + + /* Initialize the 'strh' chunk */ + + streamHdr[ 0 ].fccType = make_fourcc( "vids" ); + streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" ); + streamHdr[ 0 ].dwFlags = 0; + streamHdr[ 0 ].wPriority = 0; + streamHdr[ 0 ].wLanguage = 0; + streamHdr[ 0 ].dwInitialFrames = 0; + streamHdr[ 0 ].dwScale = 1001; + streamHdr[ 0 ].dwRate = 30000; + streamHdr[ 0 ].dwStart = 0; + streamHdr[ 0 ].dwLength = 0; + streamHdr[ 0 ].dwSuggestedBufferSize = 120008; + streamHdr[ 0 ].dwQuality = -1; + streamHdr[ 0 ].dwSampleSize = 0; + streamHdr[ 0 ].rcFrame.top = 0; + streamHdr[ 0 ].rcFrame.bottom = 0; + streamHdr[ 0 ].rcFrame.left = 0; + streamHdr[ 0 ].rcFrame.right = 0; + + bitmapinfo.biSize = sizeof( bitmapinfo ); + bitmapinfo.biWidth = 720; + bitmapinfo.biHeight = 480; + bitmapinfo.biPlanes = 1; + bitmapinfo.biBitCount = 24; + bitmapinfo.biCompression = make_fourcc( "dvsd" ); + bitmapinfo.biSizeImage = 120000; + bitmapinfo.biXPelsPerMeter = 0; + bitmapinfo.biYPelsPerMeter = 0; + bitmapinfo.biClrUsed = 0; + bitmapinfo.biClrImportant = 0; + + streamHdr[ 1 ].fccType = make_fourcc( "auds" ); + streamHdr[ 1 ].fccHandler = 0; + streamHdr[ 1 ].dwFlags = 0; + streamHdr[ 1 ].wPriority = 0; + streamHdr[ 1 ].wLanguage = 0; + streamHdr[ 1 ].dwInitialFrames = 1; + streamHdr[ 1 ].dwScale = 2 * 2; + streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2; + streamHdr[ 1 ].dwStart = 0; + streamHdr[ 1 ].dwLength = 0; + streamHdr[ 1 ].dwSuggestedBufferSize = 8192; + streamHdr[ 1 ].dwQuality = 0; + streamHdr[ 1 ].dwSampleSize = 2 * 2; + streamHdr[ 1 ].rcFrame.top = 0; + streamHdr[ 1 ].rcFrame.bottom = 0; + streamHdr[ 1 ].rcFrame.left = 0; + streamHdr[ 1 ].rcFrame.right = 0; + + break; + } + waveformatex.wFormatTag = 1; + waveformatex.nChannels = 2; + waveformatex.nSamplesPerSec = sampleFrequency; + waveformatex.nAvgBytesPerSec = sampleFrequency * 2 * 2; + waveformatex.nBlockAlign = 4; + waveformatex.wBitsPerSample = 16; + waveformatex.cbSize = 0; + + file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT ); + + /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */ + + riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list ); + hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list ); + avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list ); + + strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list ); + strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] ); + strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( BITMAPINFOHEADER ), strl_list[ 0 ] ); + if ( index_type & AVI_LARGE_INDEX ) + { + indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] ); + ix_chunk[ 0 ] = -1; + indx[ 0 ] ->dwChunkId = make_fourcc( "00dc" ); + } + + strl_list[ 1 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list ); + strh_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 1 ] ); + strf_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( WAVEFORMATEX ) - 2, strl_list[ 1 ] ); + junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, 2, strl_list[ 1 ] ); + if ( index_type & AVI_LARGE_INDEX ) + { + indx_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 1 ] ); + ix_chunk[ 1 ] = -1; + indx[ 1 ] ->dwChunkId = make_fourcc( "01wb" ); + + odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list ); + dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list ); + } + + /* align movi list to block */ + GetDirectoryEntry( hdrl_list, type, name, length, offset, parent ); + num_blocks = length / PADDING_SIZE + 1; + length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5 headers? + junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list ); + + movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list ); + + idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = make_fourcc( "7Fxx" ); + idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = 0; + idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = 0; + idx1->aIndex[ idx1->nEntriesInUse ].dwSize = 0; + idx1->nEntriesInUse++; +} + + +void AVI2File::WriteRIFF() +{ + WriteChunk( avih_chunk, ( void* ) & mainHdr ); + WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] ); + WriteChunk( strf_chunk[ 0 ], ( void* ) & bitmapinfo ); + if ( index_type & AVI_LARGE_INDEX ) + { + WriteChunk( dmlh_chunk, ( void* ) & dmlh ); + WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] ); + WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] ); + } + WriteChunk( strh_chunk[ 1 ], ( void* ) & streamHdr[ 1 ] ); + WriteChunk( strf_chunk[ 1 ], ( void* ) & waveformatex ); + if ( index_type & AVI_LARGE_INDEX ) + { + WriteChunk( indx_chunk[ 1 ], ( void* ) indx[ 1 ] ); + WriteChunk( ix_chunk[ 1 ], ( void* ) ix[ 1 ] ); + } + + if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 ) + { + int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list ); + WriteChunk( idx1_chunk, ( void* ) idx1 ); + } + RIFFFile::WriteRIFF(); +} + + +/** Write a DV video frame + + \param frame the frame to write +*/ + +#if 0 +bool AVI2File::WriteFrame( const Frame &frame ) +{ + int audio_chunk; + int frame_chunk; + int junk_chunk; + char soundbuf[ 20000 ]; + int audio_size; + int num_blocks; + FOURCC type; + FOURCC name; + off_t length; + off_t offset; + int parent; + + /* exit if no large index and 1GB reached */ + if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false ) + return false; + + /* Check if we need a new ix00 Standard Index. It has a + capacity of IX00_INDEX_SIZE frames. Whenever we exceed that + number, we need a new index. The new ix00 chunk is also + part of the movi list. */ + + if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) ) + { + FlushIndx( 0 ); + FlushIndx( 1 ); + } + + /* Write audio data if we have it */ + + audio_size = frame.ExtractAudio( soundbuf ); + if ( audio_size > 0 ) + { + audio_chunk = AddDirectoryEntry( make_fourcc( "01wb" ), 0, audio_size, movi_list ); + if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 ) + { + GetDirectoryEntry( audio_chunk, type, name, length, offset, parent ); + ix[ 1 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE; + } + WriteChunk( audio_chunk, soundbuf ); + // num_blocks = (audio_size + RIFF_HEADERSIZE) / PADDING_SIZE + 1; + // length = num_blocks * PADDING_SIZE - audio_size - 2 * RIFF_HEADERSIZE; + // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list); + // WriteChunk(junk_chunk, g_zeroes); + if ( index_type & AVI_LARGE_INDEX ) + UpdateIndx( 1, audio_chunk, audio_size / waveformatex.nChannels / 2 ); + if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 ) + UpdateIdx1( audio_chunk, 0x00 ); + streamHdr[ 1 ].dwLength += audio_size / waveformatex.nChannels / 2; + + } + + /* Write video data */ + + frame_chunk = AddDirectoryEntry( make_fourcc( "00dc" ), 0, frame.GetFrameSize(), movi_list ); + if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 ) + { + GetDirectoryEntry( frame_chunk, type, name, length, offset, parent ); + ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE; + } + WriteChunk( frame_chunk, frame.data ); + // num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1; + // length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE; + // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list); + // WriteChunk(junk_chunk, g_zeroes); + if ( index_type & AVI_LARGE_INDEX ) + UpdateIndx( 0, frame_chunk, 1 ); + if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 ) + UpdateIdx1( frame_chunk, 0x10 ); + + /* update some variables with the new frame count. */ + + if ( isUpdateIdx1 ) + ++mainHdr.dwTotalFrames; + ++streamHdr[ 0 ].dwLength; + ++dmlh[ 0 ]; + + /* Find out if the current riff list is close to 1 GByte in + size. If so, start a new (extended) RIFF. The only allowed + item in the new RIFF chunk is a movi list (with video + frames and indexes as usual). */ + + GetDirectoryEntry( riff_list, type, name, length, offset, parent ); + if ( length > 0x3f000000 ) + { + + /* write idx1 only once and before end of first GB */ + if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 ) + { + int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list ); + WriteChunk( idx1_chunk, ( void* ) idx1 ); + } + isUpdateIdx1 = false; + + if ( index_type & AVI_LARGE_INDEX ) + { + /* padding for alignment */ + GetDirectoryEntry( riff_list, type, name, length, offset, parent ); + num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1; + length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE; + if ( length > 0 ) + { + junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list ); + WriteChunk( junk_chunk, g_zeroes ); + } + + riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list ); + movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list ); + } + } + return true; +} +#endif + +void AVI1File::setDVINFO( DVINFO &info ) +{ + // do not do this until debugged audio against DirectShow + return ; + + dvinfo.dwDVAAuxSrc = info.dwDVAAuxSrc; + dvinfo.dwDVAAuxCtl = info.dwDVAAuxCtl; + dvinfo.dwDVAAuxSrc1 = info.dwDVAAuxSrc1; + dvinfo.dwDVAAuxCtl1 = info.dwDVAAuxCtl1; + dvinfo.dwDVVAuxSrc = info.dwDVVAuxSrc; + dvinfo.dwDVVAuxCtl = info.dwDVVAuxCtl; +} + + +void AVI2File::setDVINFO( DVINFO &info ) +{} + +void AVIFile::setFccHandler( FOURCC type, FOURCC handler ) +{ + for ( int i = 0; i < mainHdr.dwStreams; i++ ) + { + if ( streamHdr[ i ].fccType == type ) + { + int k, j = 0; + FOURCC strf = make_fourcc( "strf" ); + BITMAPINFOHEADER bih; + + streamHdr[ i ].fccHandler = handler; + + while ( ( k = FindDirectoryEntry( strf, j++ ) ) != -1 ) + { + ReadChunk( k, ( void* ) & bih ); + bih.biCompression = handler; + } + } + } +} + +bool AVIFile::getStreamFormat( void* data, FOURCC type ) +{ + int i, j = 0; + FOURCC strh = make_fourcc( "strh" ); + FOURCC strf = make_fourcc( "strf" ); + AVIStreamHeader avi_stream_header; + bool result = false; + + while ( ( result == false ) && ( i = FindDirectoryEntry( strh, j++ ) ) != -1 ) + { + ReadChunk( i, ( void* ) & avi_stream_header ); + if ( avi_stream_header.fccType == type ) + { + FOURCC chunkID; + int size; + + pthread_mutex_lock( &file_mutex ); + fail_neg( read( fd, &chunkID, sizeof( FOURCC ) ) ); + if ( chunkID == strf ) + { + fail_neg( read( fd, &size, sizeof( int ) ) ); + fail_neg( read( fd, data, size ) ); + result = true; + } + pthread_mutex_unlock( &file_mutex ); + } + } + return result; +} diff --git a/src/modules/kino/avi.h b/src/modules/kino/avi.h new file mode 100644 index 00000000..864dfe17 --- /dev/null +++ b/src/modules/kino/avi.h @@ -0,0 +1,400 @@ +/* +* avi.h library for AVI file format i/o +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* 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. +* +* Tag: $Name$ +* +* Change log: +* +* $Log$ +* Revision 1.1 2005/04/15 14:28:26 lilo_booter +* Initial version +* +* Revision 1.16 2005/04/01 23:43:10 ddennedy +* apply endian fixes from Daniel Kobras +* +* Revision 1.15 2004/10/11 01:37:11 ddennedy +* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script +* +* Revision 1.14 2003/11/25 23:00:52 ddennedy +* cleanup and a few bugfixes +* +* Revision 1.13 2003/10/21 16:34:32 ddennedy +* GNOME2 port phase 1: initial checkin +* +* Revision 1.11.2.5 2003/07/24 14:13:57 ddennedy +* support for distinct audio stream in type2 AVI and Quicktime; support for more DV FOURCCs +* +* Revision 1.11.2.4 2003/06/10 23:53:36 ddennedy +* Daniel Kobras' WriteFrame error handling and automatic OpenDML, bugfixes in scene list updates, export AV/C Record +* +* Revision 1.11.2.3 2003/02/20 21:59:57 ddennedy +* bugfixes to capture and AVI +* +* Revision 1.11.2.2 2003/01/13 05:15:31 ddennedy +* added More Info panel and supporting methods +* +* Revision 1.11.2.1 2002/11/25 04:48:31 ddennedy +* bugfix to report errors when loading files +* +* Revision 1.11 2002/10/08 07:46:41 ddennedy +* AVI bugfixes, compatibility, optimization, warn bad file in capture and export dv file, allow no mplex +* +* Revision 1.10 2002/05/17 08:04:25 ddennedy +* revert const-ness of Frame references in Frame, FileHandler, and AVI classes +* +* Revision 1.9 2002/05/15 04:39:35 ddennedy +* bugfixes to dv2 AVI write, audio export, Xv init +* +* Revision 1.8 2002/04/29 05:09:22 ddennedy +* raw dv file support, Frame::ExtractAudio uses libdv, audioScrub prefs +* +* Revision 1.7 2002/04/09 06:53:42 ddennedy +* cleanup, new libdv 0.9.5, large AVI, dnd storyboard +* +* Revision 1.7 2002/03/25 21:34:25 arne +* Support for large (64 bit) files mostly completed +* +* Revision 1.6 2002/03/10 13:29:41 arne +* more changes for 64 bit access +* +* Revision 1.5 2002/03/09 17:59:28 arne +* moved index routines to AVIFile +* +* Revision 1.4 2002/03/09 10:26:26 arne +* improved constructors and assignment operator +* +* Revision 1.3 2002/03/09 08:55:57 arne +* moved a few variables to AVIFile +* +* Revision 1.2 2002/03/04 19:22:43 arne +* updated to latest Kino avi code +* +* Revision 1.1.1.1 2002/03/03 19:08:08 arne +* import of version 1.01 +* +*/ + +/** Common AVI declarations + + Some of this comes from the public domain AVI specification, which + explains the microsoft-style definitions. + + \file avi.h +*/ + +#ifndef _AVI_H +#define _AVI_H 1 + +#include "riff.h" + +#define PACKED(x) __attribute__((packed)) x + +#define AVI_SMALL_INDEX (0x01) +#define AVI_LARGE_INDEX (0x02) +#define AVI_INDEX_OF_INDEXES (0x00) +#define AVI_INDEX_OF_CHUNKS (0x01) +#define AVI_INDEX_2FIELD (0x01) + +enum { AVI_PAL, AVI_NTSC, AVI_AUDIO_48KHZ, AVI_AUDIO_44KHZ, AVI_AUDIO_32KHZ }; + +/** Declarations of the main AVI file header + + The contents of this struct goes into the 'avih' chunk. */ + +typedef struct +{ + /// frame display rate (or 0L) + DWORD dwMicroSecPerFrame; + + /// max. transfer rate + DWORD dwMaxBytesPerSec; + + /// pad to multiples of this size, normally 2K + DWORD dwPaddingGranularity; + + /// the ever-present flags + DWORD dwFlags; + + /// # frames in file + DWORD dwTotalFrames; + DWORD dwInitialFrames; + DWORD dwStreams; + DWORD dwSuggestedBufferSize; + + DWORD dwWidth; + DWORD dwHeight; + + DWORD dwReserved[ 4 ]; +} +PACKED(MainAVIHeader); + +typedef struct +{ + WORD top, bottom, left, right; +} +PACKED(RECT); + +/** Declaration of a stream header + + The contents of this struct goes into the 'strh' header. */ + +typedef struct +{ + FOURCC fccType; + FOURCC fccHandler; + DWORD dwFlags; /* Contains AVITF_* flags */ + WORD wPriority; + WORD wLanguage; + DWORD dwInitialFrames; + DWORD dwScale; + DWORD dwRate; /* dwRate / dwScale == samples/second */ + DWORD dwStart; + DWORD dwLength; /* In units above... */ + DWORD dwSuggestedBufferSize; + DWORD dwQuality; + DWORD dwSampleSize; + RECT rcFrame; +} +PACKED(AVIStreamHeader); + +typedef struct +{ + DWORD dwDVAAuxSrc; + DWORD dwDVAAuxCtl; + DWORD dwDVAAuxSrc1; + DWORD dwDVAAuxCtl1; + DWORD dwDVVAuxSrc; + DWORD dwDVVAuxCtl; + DWORD dwDVReserved[ 2 ]; +} +PACKED(DVINFO); + +typedef struct +{ + DWORD biSize; + LONG biWidth; + LONG biHeight; + WORD biPlanes; + WORD biBitCount; + DWORD biCompression; + DWORD biSizeImage; + LONG biXPelsPerMeter; + LONG biYPelsPerMeter; + DWORD biClrUsed; + DWORD biClrImportant; +} +PACKED(BITMAPINFOHEADER); + +typedef struct +{ + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; + WORD dummy; +} +PACKED(WAVEFORMATEX); + +typedef struct +{ + WORD wLongsPerEntry; + BYTE bIndexSubType; + BYTE bIndexType; + DWORD nEntriesInUse; + FOURCC dwChunkId; + DWORD dwReserved[ 3 ]; + struct avisuperindex_entry + { + QUADWORD qwOffset; + DWORD dwSize; + DWORD dwDuration; + } + aIndex[ 2014 ]; +} +PACKED(AVISuperIndex); + +typedef struct +{ + WORD wLongsPerEntry; + BYTE bIndexSubType; + BYTE bIndexType; + DWORD nEntriesInUse; + FOURCC dwChunkId; + QUADWORD qwBaseOffset; + DWORD dwReserved; + struct avifieldindex_entry + { + DWORD dwOffset; + DWORD dwSize; + } + aIndex[ 4028 ]; +} +PACKED(AVIStdIndex); + +typedef struct +{ + struct avisimpleindex_entry + { + FOURCC dwChunkId; + DWORD dwFlags; + DWORD dwOffset; + DWORD dwSize; + } + aIndex[ 20000 ]; + DWORD nEntriesInUse; +} +PACKED(AVISimpleIndex); + +typedef struct +{ + DWORD dirEntryType; + DWORD dirEntryName; + DWORD dirEntryLength; + size_t dirEntryOffset; + int dirEntryWrittenFlag; + int dirEntryParentList; +} +AviDirEntry; + + +/** base class for all AVI type files + + It contains methods and members which are the same in all AVI type files regardless of the particular compression, number + of streams etc. + + The AVIFile class also contains methods for handling several indexes to the video frame content. */ + +class AVIFile : public RIFFFile +{ +public: + AVIFile(); + AVIFile( const AVIFile& ); + virtual ~AVIFile(); + virtual AVIFile& operator=( const AVIFile& ); + + virtual void Init( int format, int sampleFrequency, int indexType ); + virtual int GetDVFrameInfo( off_t &offset, int &size, int frameNum ); + virtual int GetFrameInfo( off_t &offset, int &size, int frameNum, FOURCC chunkID ); + virtual int GetDVFrame( uint8_t *data, int frameNum ); + virtual int getFrame( void *data, int frameNum, FOURCC chunkID ); + virtual int GetTotalFrames() const; + virtual void PrintDirectoryEntryData( const RIFFDirEntry &entry ) const; + //virtual bool WriteFrame( const Frame &frame ) { return false; } + virtual void ParseList( int parent ); + virtual void ParseRIFF( void ); + virtual void ReadIndex( void ); + virtual void WriteRIFF( void ) + { } + virtual void FlushIndx( int stream ); + virtual void UpdateIndx( int stream, int chunk, int duration ); + virtual void UpdateIdx1( int chunk, int flags ); + virtual bool verifyStreamFormat( FOURCC type ); + virtual bool verifyStream( FOURCC type ); + virtual bool isOpenDML( void ); + virtual void setDVINFO( DVINFO& ) + { } + virtual void setFccHandler( FOURCC type, FOURCC handler ); + virtual bool getStreamFormat( void* data, FOURCC type ); + +protected: + MainAVIHeader mainHdr; + AVISimpleIndex *idx1; + int file_list; + int riff_list; + int hdrl_list; + int avih_chunk; + int movi_list; + int junk_chunk; + int idx1_chunk; + + AVIStreamHeader streamHdr[ 2 ]; + AVISuperIndex *indx[ 2 ]; + AVIStdIndex *ix[ 2 ]; + int indx_chunk[ 2 ]; + int ix_chunk[ 2 ]; + int strl_list[ 2 ]; + int strh_chunk[ 2 ]; + int strf_chunk[ 2 ]; + + int index_type; + int current_ix00; + + DWORD dmlh[ 62 ]; + int odml_list; + int dmlh_chunk; + bool isUpdateIdx1; + +}; + + +/** writing Type 1 DV AVIs + +*/ + +class AVI1File : public AVIFile +{ +public: + AVI1File(); + virtual ~AVI1File(); + + virtual void Init( int format, int sampleFrequency, int indexType ); + //virtual bool WriteFrame( const Frame &frame ); + virtual void WriteRIFF( void ); + virtual void setDVINFO( DVINFO& ); + +private: + DVINFO dvinfo; + + AVI1File( const AVI1File& ); + AVI1File& operator=( const AVI1File& ); +}; + + +/** writing Type 2 (separate audio data) DV AVIs + +This file type contains both audio and video tracks. It is therefore more compatible +to certain Windows programs, which expect any AVI having both audio and video tracks. +The video tracks contain the raw DV data (as in type 1) and the extracted audio tracks. + +Note that because the DV data contains audio information anyway, this means duplication +of data and a slight increase of file size. + +*/ + +class AVI2File : public AVIFile +{ +public: + AVI2File(); + virtual ~AVI2File(); + + virtual void Init( int format, int sampleFrequency, int indexType ); + //virtual bool WriteFrame( const Frame &frame ); + virtual void WriteRIFF( void ); + virtual void setDVINFO( DVINFO& ); + +private: + BITMAPINFOHEADER bitmapinfo; + WAVEFORMATEX waveformatex; + + AVI2File( const AVI2File& ); + AVI2File& operator=( const AVI2File& ); +}; +#endif diff --git a/src/modules/kino/configure b/src/modules/kino/configure new file mode 100755 index 00000000..d13e2ecb --- /dev/null +++ b/src/modules/kino/configure @@ -0,0 +1,18 @@ +#!/bin/sh + +if [ "$help" != "1" ] +then + # Entirely optional... + lqt-config --prefix > /dev/null 2>&1 + lqt_disabled=$? + + echo > config.h + [ "$lqt_disabled" == "0" ] && echo "#define HAVE_LIBQUICKTIME" >> config.h + echo > config.mak + [ "$lqt_disabled" == "0" ] && echo "HAVE_LIBQUICKTIME=1" >> config.mak + + [ "$lqt_disabled" != "0" ] && echo "- libquicktime not found: only enabling dv avi support" + + echo "kino libmltkino$LIBSUF" >> ../producers.dat +fi + diff --git a/src/modules/kino/endian_types.h b/src/modules/kino/endian_types.h new file mode 100644 index 00000000..709ccfac --- /dev/null +++ b/src/modules/kino/endian_types.h @@ -0,0 +1,265 @@ +/* + * + * Quick hack to handle endianness and word length issues. + * Defines _le, _be, and _ne variants to standard ISO types + * like int32_t, that are stored in little-endian, big-endian, + * and native-endian byteorder in memory, respectively. + * Caveat: int32_le_t and friends cannot be used in vararg + * functions like printf() without an explicit cast. + * + * Copyright (c) 2003-2005 Daniel Kobras + * + * 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. + */ + +#ifndef _ENDIAN_TYPES_H +#define _ENDIAN_TYPES_H + +/* Needed for BYTE_ORDER and BIG/LITTLE_ENDIAN macros. */ +#ifndef _BSD_SOURCE +# define _BSD_SOURCE +# include +# undef _BSD_SOURCE +#else +# include +#endif + +#include +#include + +static inline int8_t bswap(const int8_t& x) +{ + return x; +} + +static inline u_int8_t bswap(const u_int8_t& x) +{ + return x; +} + +static inline int16_t bswap(const int16_t& x) +{ + return bswap_16(x); +} + +static inline u_int16_t bswap(const u_int16_t& x) +{ + return bswap_16(x); +} + +static inline int32_t bswap(const int32_t& x) +{ + return bswap_32(x); +} + +static inline u_int32_t bswap(const u_int32_t& x) +{ + return bswap_32(x); +} + +static inline int64_t bswap(const int64_t& x) +{ + return bswap_64(x); +} + +static inline u_int64_t bswap(const u_int64_t& x) +{ + return bswap_64(x); +} + +#define le_to_cpu cpu_to_le +#define be_to_cpu cpu_to_be + +template static inline T cpu_to_le(const T& x) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + return x; +#else + return bswap(x); +#endif +} + +template static inline T cpu_to_be(const T& x) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + return bswap(x); +#else + return x; +#endif +} + +template class le_t { + T m; + T read() const { + return le_to_cpu(m); + }; + void write(const T& n) { + m = cpu_to_le(n); + }; +public: + le_t(void) { + m = 0; + }; + le_t(const T& o) { + write(o); + }; + operator T() const { + return read(); + }; + le_t operator++() { + write(read() + 1); + return *this; + }; + le_t operator++(int) { + write(read() + 1); + return *this; + }; + le_t operator--() { + write(read() - 1); + return *this; + }; + le_t operator--(int) { + write(read() - 1); + return *this; + }; + le_t& operator+=(const T& t) { + write(read() + t); + return *this; + }; + le_t& operator-=(const T& t) { + write(read() - t); + return *this; + }; + le_t& operator&=(const le_t& t) { + m &= t.m; + return *this; + }; + le_t& operator|=(const le_t& t) { + m |= t.m; + return *this; + }; +} __attribute__((packed)); + +/* Just copy-and-pasted from le_t. Too lazy to do it right. */ + +template class be_t { + T m; + T read() const { + return be_to_cpu(m); + }; + void write(const T& n) { + m = cpu_to_be(n); + }; +public: + be_t(void) { + m = 0; + }; + be_t(const T& o) { + write(o); + }; + operator T() const { + return read(); + }; + be_t operator++() { + write(read() + 1); + return *this; + }; + be_t operator++(int) { + write(read() + 1); + return *this; + }; + be_t operator--() { + write(read() - 1); + return *this; + }; + be_t operator--(int) { + write(read() - 1); + return *this; + }; + be_t& operator+=(const T& t) { + write(read() + t); + return *this; + }; + be_t& operator-=(const T& t) { + write(read() - t); + return *this; + }; + be_t& operator&=(const be_t& t) { + m &= t.m; + return *this; + }; + be_t& operator|=(const be_t& t) { + m |= t.m; + return *this; + }; +} __attribute__((packed)); + +/* Define types of native endianness similar to the little and big endian + * versions below. Not really necessary but useful occasionally to emphasize + * endianness of data. + */ + +typedef int8_t int8_ne_t; +typedef int16_t int16_ne_t; +typedef int32_t int32_ne_t; +typedef int64_t int64_ne_t; +typedef u_int8_t u_int8_ne_t; +typedef u_int16_t u_int16_ne_t; +typedef u_int32_t u_int32_ne_t; +typedef u_int64_t u_int64_ne_t; + + +/* The classes work on their native endianness as well, but obviously + * introduce some overhead. Use the faster typedefs to native types + * therefore, unless you're debugging. + */ + +#if BYTE_ORDER == LITTLE_ENDIAN +typedef int8_ne_t int8_le_t; +typedef int16_ne_t int16_le_t; +typedef int32_ne_t int32_le_t; +typedef int64_ne_t int64_le_t; +typedef u_int8_ne_t u_int8_le_t; +typedef u_int16_ne_t u_int16_le_t; +typedef u_int32_ne_t u_int32_le_t; +typedef u_int64_ne_t u_int64_le_t; +typedef int8_t int8_be_t; +typedef be_t int16_be_t; +typedef be_t int32_be_t; +typedef be_t int64_be_t; +typedef u_int8_t u_int8_be_t; +typedef be_t u_int16_be_t; +typedef be_t u_int32_be_t; +typedef be_t u_int64_be_t; +#else +typedef int8_ne_t int8_be_t; +typedef int16_ne_t int16_be_t; +typedef int32_ne_t int32_be_t; +typedef int64_ne_t int64_be_t; +typedef u_int8_ne_t u_int8_be_t; +typedef u_int16_ne_t u_int16_be_t; +typedef u_int32_ne_t u_int32_be_t; +typedef u_int64_ne_t u_int64_be_t; +typedef int8_t int8_le_t; +typedef le_t int16_le_t; +typedef le_t int32_le_t; +typedef le_t int64_le_t; +typedef u_int8_t u_int8_le_t; +typedef le_t u_int16_le_t; +typedef le_t u_int32_le_t; +typedef le_t u_int64_le_t; +#endif + +#endif diff --git a/src/modules/kino/error.cc b/src/modules/kino/error.cc new file mode 100644 index 00000000..e2b8510e --- /dev/null +++ b/src/modules/kino/error.cc @@ -0,0 +1,103 @@ +/* +* error.cc Error handling +* Copyright (C) 2000 Arne Schirmacher +* +* 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. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +// C++ includes + +#include +#include +#include +#include + +using std::ostringstream; +using std::string; +using std::endl; +using std::ends; +using std::cerr; + +// C includes + +#include +#include + +// local includes + +#include "error.h" + +void real_fail_neg( int eval, const char *eval_str, const char *func, const char *file, int line ) +{ + if ( eval < 0 ) + { + string exc; + ostringstream sb; + + sb << file << ":" << line << ": In function \"" << func << "\": \"" << eval_str << "\" evaluated to " << eval; + if ( errno != 0 ) + sb << endl << file << ":" << line << ": errno: " << errno << " (" << strerror( errno ) << ")"; + sb << ends; + exc = sb.str(); + cerr << exc << endl; + throw exc; + } +} + + +/** error handler for NULL result codes + + Whenever this is called with a NULL argument, it will throw an + exception. Typically used with functions like malloc() and new(). + +*/ + +void real_fail_null( const void *eval, const char *eval_str, const char *func, const char *file, int line ) +{ + if ( eval == NULL ) + { + + string exc; + ostringstream sb; + + sb << file << ":" << line << ": In function \"" << func << "\": " << eval_str << " is NULL" << ends; + exc = sb.str(); + cerr << exc << endl; + throw exc; + } +} + + +void real_fail_if( bool eval, const char *eval_str, const char *func, const char *file, int line ) +{ + if ( eval == true ) + { + + string exc; + ostringstream sb; + + sb << file << ":" << line << ": In function \"" << func << "\": condition \"" << eval_str << "\" is true"; + if ( errno != 0 ) + sb << endl << file << ":" << line << ": errno: " << errno << " (" << strerror( errno ) << ")"; + sb << ends; + exc = sb.str(); + cerr << exc << endl; + throw exc; + } +} diff --git a/src/modules/kino/error.h b/src/modules/kino/error.h new file mode 100644 index 00000000..9c048941 --- /dev/null +++ b/src/modules/kino/error.h @@ -0,0 +1,51 @@ +/* +* error.h Error handling +* Copyright (C) 2000 Arne Schirmacher +* +* 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. +*/ + +#ifndef _ERROR_H +#define _ERROR_H 1 + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* + * Should check for gcc/g++ and version > 2.6 I suppose + */ +#ifndef __ASSERT_FUNCTION +# define __ASSERT_FUNCTION __PRETTY_FUNCTION__ +#endif + +#define fail_neg(eval) real_fail_neg (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__) +#define fail_null(eval) real_fail_null (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__) +#define fail_if(eval) real_fail_if (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__) + + void real_fail_neg ( int eval, const char * eval_str, const char * func, const char * file, int line ); + void real_fail_null ( const void * eval, const char * eval_str, const char * func, const char * file, int line ); + void real_fail_if ( bool eval, const char * eval_str, const char * func, const char * file, int line ); + + extern void sigpipe_clear( ); + extern int sigpipe_get( ); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/modules/kino/factory.c b/src/modules/kino/factory.c new file mode 100644 index 00000000..bbae5055 --- /dev/null +++ b/src/modules/kino/factory.c @@ -0,0 +1,46 @@ +/* + * factory.c -- the factory method interfaces + * Copyright (C) 2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 + +#include "producer_kino.h" + +void *mlt_create_producer( char *id, void *arg ) +{ + if ( !strcmp( id, "kino" ) ) + return producer_kino_init( arg ); + return NULL; +} + +void *mlt_create_filter( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_transition( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_consumer( char *id, void *arg ) +{ + return NULL; +} + diff --git a/src/modules/kino/filehandler.cc b/src/modules/kino/filehandler.cc new file mode 100644 index 00000000..740ee560 --- /dev/null +++ b/src/modules/kino/filehandler.cc @@ -0,0 +1,913 @@ +/* +* filehandler.cc -- saving DV data into different file formats +* Copyright (C) 2000 Arne Schirmacher +* +* 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 +#include +#include +#include + +using std::cerr; +using std::endl; +using std::ostringstream; +using std::setw; +using std::setfill; + +#include +#include +#include +#include +#include +#include +#include + +#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( 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( 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 diff --git a/src/modules/kino/filehandler.h b/src/modules/kino/filehandler.h new file mode 100644 index 00000000..66579519 --- /dev/null +++ b/src/modules/kino/filehandler.h @@ -0,0 +1,217 @@ +/* +* filehandler.h +* Copyright (C) 2000 Arne Schirmacher +* +* 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. +*/ + +#ifndef _FILEHANDLER_H +#define _FILEHANDLER_H + +// enum { PAL_FORMAT, NTSC_FORMAT, AVI_DV1_FORMAT, AVI_DV2_FORMAT, QT_FORMAT, RAW_FORMAT, TEST_FORMAT, UNDEFINED }; + +#include +using std::vector; + +#include +using std::string; + +#include "riff.h" +#include "avi.h" +#include +#include + +enum { AVI, PLAYLIST, RAW_DV, QT, UNKNOWN_FORMAT }; +enum { PAL_FORMAT, NTSC_FORMAT, AVI_DV1_FORMAT, AVI_DV2_FORMAT, QT_FORMAT, RAW_FORMAT, TEST_FORMAT, UNDEFINED }; +enum { DISPLAY_XX, DISPLAY_GDKRGB, DISPLAY_GDKRGB32, DISPLAY_XV, DISPLAY_SDL }; + +enum { NORM_UNSPECIFIED=0, NORM_PAL=1, NORM_NTSC=2 }; +enum { AUDIO_32KHZ=0, AUDIO_44KHZ=1, AUDIO_48KHZ=2 }; +enum { ASPECT_43=0, ASPECT_169=1 }; + +enum FileCaptureMode { + CAPTURE_IGNORE, + CAPTURE_FRAME_APPEND, + CAPTURE_FRAME_INSERT, + CAPTURE_MOVIE_APPEND +}; + +class FileTracker +{ +protected: + FileTracker(); + ~FileTracker(); +public: + static FileTracker &GetInstance( ); + void SetMode( FileCaptureMode ); + FileCaptureMode GetMode( ); + unsigned int Size(); + char *Get( int ); + void Add( const char * ); + void Clear( ); +private: + static FileTracker *instance; + vector list; + FileCaptureMode mode; +}; + +class FileHandler +{ +public: + + FileHandler(); + virtual ~FileHandler(); + + virtual bool GetAutoSplit() const; + virtual bool GetTimeStamp() const; + virtual string GetBaseName() const; + virtual string GetExtension() const; + virtual int GetMaxFrameCount() const; + virtual off_t GetMaxFileSize() const; + virtual off_t GetFileSize() = 0; + virtual int GetTotalFrames() = 0; + virtual string GetFilename() const; + + virtual void SetAutoSplit( bool ); + virtual void SetTimeStamp( bool ); + virtual void SetBaseName( const string& base ); + virtual void SetMaxFrameCount( int ); + virtual void SetEveryNthFrame( int ); + virtual void SetMaxFileSize( off_t ); + //virtual void SetSampleFrame( const Frame& sample ); + + //virtual bool WriteFrame( const Frame& frame ); + virtual bool FileIsOpen() = 0; + virtual bool Create( const string& filename ) = 0; + //virtual int Write( const Frame& frame ) = 0; + virtual int Close() = 0; + virtual bool Done( void ); + + virtual bool Open( const char *s ) = 0; + virtual int GetFrame( uint8_t *data, int frameNum ) = 0; + int GetFramesWritten() const + { + return framesWritten; + } + +protected: + bool done; + bool autoSplit; + bool timeStamp; + int maxFrameCount; + int framesWritten; + int everyNthFrame; + int framesToSkip; + off_t maxFileSize; + string base; + string extension; + string filename; +}; + + +class RawHandler: public FileHandler +{ +public: + int fd; + + RawHandler(); + ~RawHandler(); + + bool FileIsOpen(); + bool Create( const string& filename ); + //int Write( const Frame& frame ); + int Close(); + off_t GetFileSize(); + int GetTotalFrames(); + bool Open( const char *s ); + int GetFrame( uint8_t *data, int frameNum ); +private: + int numBlocks; +}; + + +class AVIHandler: public FileHandler +{ +public: + AVIHandler( int format = AVI_DV1_FORMAT ); + ~AVIHandler(); + + //void SetSampleFrame( const Frame& sample ); + bool FileIsOpen(); + bool Create( const string& filename ); + //int Write( const Frame& frame ); + int Close(); + off_t GetFileSize(); + int GetTotalFrames(); + bool Open( const char *s ); + int GetFrame( uint8_t *data, int frameNum ); + bool GetOpenDML() const; + void SetOpenDML( bool ); + int GetFormat() const + { + return aviFormat; + } + +protected: + AVIFile *avi; + int aviFormat; + //AudioInfo audioInfo; + //VideoInfo videoInfo; + bool isOpenDML; + DVINFO dvinfo; + FOURCC fccHandler; + int channels; + bool isFullyInitialized; + int16_t *audioBuffer; + int16_t *audioChannels[ 4 ]; +}; + + +#ifdef HAVE_LIBQUICKTIME +#include + +class QtHandler: public FileHandler +{ +public: + QtHandler(); + ~QtHandler(); + + bool FileIsOpen(); + bool Create( const string& filename ); + //int Write( const Frame& frame ); + int Close(); + off_t GetFileSize(); + int GetTotalFrames(); + bool Open( const char *s ); + int GetFrame( uint8_t *data, int frameNum ); + void AllocateAudioBuffers(); + +private: + quicktime_t *fd; + long samplingRate; + int samplesPerBuffer; + int channels; + bool isFullyInitialized; + unsigned int audioBufferSize; + int16_t *audioBuffer; + short int** audioChannelBuffer; + + void Init(); + inline void DeinterlaceStereo16( void* pInput, int iBytes, void* pLOutput, void* pROutput ); + +}; +#endif + +#endif diff --git a/src/modules/kino/gpl b/src/modules/kino/gpl new file mode 100644 index 00000000..e69de29b diff --git a/src/modules/kino/kino_wrapper.cc b/src/modules/kino/kino_wrapper.cc new file mode 100644 index 00000000..85f87c0f --- /dev/null +++ b/src/modules/kino/kino_wrapper.cc @@ -0,0 +1,108 @@ +/* + * kino_wrapper.cc -- c wrapper for kino file handler + * Copyright (C) 2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include "kino_wrapper.h" +#include "filehandler.h" + +extern "C" +{ + +#include + +struct kino_wrapper_s +{ + FileHandler *handler; + int is_pal; +}; + +kino_wrapper kino_wrapper_init( ) +{ + kino_wrapper self = ( kino_wrapper )malloc( sizeof( kino_wrapper_s ) ); + if ( self != NULL ) + self->handler = NULL; + return self; +} + +int kino_wrapper_open( kino_wrapper self, char *src ) +{ + if ( self != NULL ) + { + // Rough file determination based on file type + if ( strncasecmp( strrchr( src, '.' ), ".avi", 4 ) == 0 ) + self->handler = new AVIHandler( ); + else if ( strncasecmp( strrchr( src, '.' ), ".dv", 3 ) == 0 || strncasecmp( strrchr( src, '.' ), ".dif", 4 ) == 0 ) + self->handler = new RawHandler( ); + #ifdef HAVE_LIBQUICKTIME + else if ( strncasecmp( strrchr( src, '.' ), ".mov", 4 ) == 0 ) + self->handler = new QtHandler( ); + #endif + + // Open the file if we have a handler + if ( self->handler != NULL ) + if ( !self->handler->Open( src ) ) + self = NULL; + + // Check the first frame to see if it's PAL or NTSC + if ( self != NULL && self->handler != NULL ) + { + uint8_t *data = ( uint8_t * )mlt_pool_alloc( 144000 ); + if ( self->handler->GetFrame( data, 0 ) == 0 ) + self->is_pal = data[3] & 0x80; + else + self = NULL; + mlt_pool_release( data ); + } + } + + return kino_wrapper_is_open( self ); +} + +int kino_wrapper_get_frame_count( kino_wrapper self ) +{ + return self != NULL && self->handler != NULL ? self->handler->GetTotalFrames( ) : 0; +} + +int kino_wrapper_is_open( kino_wrapper self ) +{ + return self != NULL && self->handler != NULL ? self->handler->FileIsOpen( ) : 0; +} + +int kino_wrapper_is_pal( kino_wrapper self ) +{ + return self != NULL ? self->is_pal : 0; +} + +int kino_wrapper_get_frame( kino_wrapper self, uint8_t *data, int index ) +{ + return self != NULL && self->handler != NULL ? !self->handler->GetFrame( data, index ) : 0; +} + +void kino_wrapper_close( kino_wrapper self ) +{ + if ( self ) + delete self->handler; + free( self ); +} + +} + + diff --git a/src/modules/kino/kino_wrapper.h b/src/modules/kino/kino_wrapper.h new file mode 100644 index 00000000..0e73b217 --- /dev/null +++ b/src/modules/kino/kino_wrapper.h @@ -0,0 +1,45 @@ +/* + * kino_wrapper.h -- c wrapper for kino file handler + * Copyright (C) 2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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. + */ + +#ifndef MLT_PRODUCER_KINO_WRAPPER_H_ +#define MLT_PRODUCER_KINO_WRAPPER_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct kino_wrapper_s *kino_wrapper; + +extern kino_wrapper kino_wrapper_init( ); +extern int kino_wrapper_open( kino_wrapper, char * ); +extern int kino_wrapper_is_open( kino_wrapper ); +extern int kino_wrapper_is_pal( kino_wrapper ); +extern int kino_wrapper_get_frame_count( kino_wrapper ); +extern int kino_wrapper_get_frame( kino_wrapper, uint8_t *, int ); +extern void kino_wrapper_close( kino_wrapper ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/modules/kino/producer_kino.c b/src/modules/kino/producer_kino.c new file mode 100644 index 00000000..de876cb3 --- /dev/null +++ b/src/modules/kino/producer_kino.c @@ -0,0 +1,144 @@ +/* + * producer_kino.c -- a DV file format parser + * Copyright (C) 2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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 +#include "producer_kino.h" +#include +#include +#include +#include "kino_wrapper.h" + +/* NB: This is an abstract producer - it provides no codec support whatsoever. */ + +#define FRAME_SIZE_525_60 10 * 150 * 80 +#define FRAME_SIZE_625_50 12 * 150 * 80 + +typedef struct producer_kino_s *producer_kino; + +struct producer_kino_s +{ + struct mlt_producer_s parent; + kino_wrapper wrapper; +}; + +static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index ); +static void producer_close( mlt_producer parent ); + +mlt_producer producer_kino_init( char *filename ) +{ + kino_wrapper wrapper = kino_wrapper_init( ); + + if ( kino_wrapper_open( wrapper, filename ) ) + { + producer_kino this = calloc( sizeof( struct producer_kino_s ), 1 ); + + if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) + { + mlt_producer producer = &this->parent; + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); + double fps = kino_wrapper_is_pal( wrapper ) ? 25 : 30000.0 / 1001.0; + + // Assign the wrapper + this->wrapper = wrapper; + + // Pass wrapper properties (frame rate, count etc) + mlt_properties_set_position( properties, "length", kino_wrapper_get_frame_count( wrapper ) ); + mlt_properties_set_position( properties, "in", 0 ); + mlt_properties_set_position( properties, "out", kino_wrapper_get_frame_count( wrapper ) - 1 ); + mlt_properties_set_double( properties, "real_fps", fps ); + + // Register transport implementation with the producer + producer->close = ( mlt_destructor )producer_close; + + // Register our get_frame implementation with the producer + producer->get_frame = producer_get_frame; + + // Return the producer + return producer; + } + free( this ); + } + + kino_wrapper_close( wrapper ); + + return NULL; +} + +static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) +{ + producer_kino this = producer->child; + uint8_t *data = mlt_pool_alloc( FRAME_SIZE_625_50 ); + + // Obtain the current frame number + uint64_t position = mlt_producer_frame( producer ); + + // Create an empty frame + *frame = mlt_frame_init( ); + + // Seek and fetch + if ( kino_wrapper_get_frame( this->wrapper, data, position ) ) + { + // Get the frames properties + mlt_properties properties = MLT_FRAME_PROPERTIES( *frame ); + + // Determine if we're PAL or NTSC + int is_pal = kino_wrapper_is_pal( this->wrapper ); + + // Pass the dv data + mlt_properties_set_data( properties, "dv_data", data, FRAME_SIZE_625_50, ( mlt_destructor )mlt_pool_release, NULL ); + + // Update other info on the frame + mlt_properties_set_int( properties, "width", 720 ); + mlt_properties_set_int( properties, "height", is_pal ? 576 : 480 ); + mlt_properties_set_int( properties, "top_field_first", is_pal ? 0 : ( data[ 5 ] & 0x07 ) == 0 ? 0 : 1 ); + } + else + { + mlt_pool_release( data ); + } + + // Update timecode on the frame we're creating + mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); + + // Calculate the next timecode + mlt_producer_prepare_next( producer ); + + return 0; +} + +static void producer_close( mlt_producer parent ) +{ + if ( parent != NULL ) + { + // Obtain this + producer_kino this = parent->child; + + // Close the file + if ( this != NULL ) + kino_wrapper_close( this->wrapper ); + + // Close the parent + parent->close = NULL; + mlt_producer_close( parent ); + + // Free the memory + free( this ); + } +} diff --git a/src/modules/kino/producer_kino.h b/src/modules/kino/producer_kino.h new file mode 100644 index 00000000..9f6f69af --- /dev/null +++ b/src/modules/kino/producer_kino.h @@ -0,0 +1,28 @@ +/* + * producer_kino.h -- a DV file format parser + * Copyright (C) 2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * 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. + */ + +#ifndef _PRODUCER_KINO_H_ +#define _PRODUCER_KINO_H_ + +#include + +extern mlt_producer producer_kino_init( char *filename ); + +#endif diff --git a/src/modules/kino/riff.cc b/src/modules/kino/riff.cc new file mode 100644 index 00000000..bc616dda --- /dev/null +++ b/src/modules/kino/riff.cc @@ -0,0 +1,705 @@ +/* +* riff.cc library for RIFF file format i/o +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* 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. +* +* Tag: $Name$ +* +* Change log: +* +* $Log$ +* Revision 1.1 2005/04/15 14:28:26 lilo_booter +* Initial version +* +* Revision 1.18 2005/04/01 23:43:10 ddennedy +* apply endian fixes from Daniel Kobras +* +* Revision 1.17 2004/10/11 01:37:11 ddennedy +* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script +* +* Revision 1.16 2003/11/25 23:01:24 ddennedy +* cleanup and a few bugfixes +* +* Revision 1.15 2003/10/21 16:34:34 ddennedy +* GNOME2 port phase 1: initial checkin +* +* Revision 1.13.2.3 2003/08/26 20:39:00 ddennedy +* relocate mutex unlock and add assert includes +* +* Revision 1.13.2.2 2003/01/28 12:54:13 lilo_booter +* New 'no change' image transition +* +* Revision 1.13.2.1 2002/11/25 04:48:31 ddennedy +* bugfix to report errors when loading files +* +* Revision 1.13 2002/09/13 06:49:49 ddennedy +* build update, cleanup, bugfixes +* +* Revision 1.12 2002/04/21 06:36:40 ddennedy +* kindler avc and 1394 bus reset support in catpure page, honor max file size +* +* Revision 1.11 2002/04/09 06:53:42 ddennedy +* cleanup, new libdv 0.9.5, large AVI, dnd storyboard +* +* Revision 1.4 2002/03/25 21:34:25 arne +* Support for large (64 bit) files mostly completed +* +* Revision 1.3 2002/03/10 21:28:29 arne +* release 1.1b1, 64 bit support for type 1 avis +* +* Revision 1.2 2002/03/04 19:22:43 arne +* updated to latest Kino avi code +* +* Revision 1.1.1.1 2002/03/03 19:08:08 arne +* import of version 1.01 +* +*/ + +#include "config.h" + +// C++ includes + +#include +//#include +#include +#include +#include + +using std::cout; +using std::hex; +using std::dec; +using std::setw; +using std::setfill; +using std::endl; + +// C includes + +#include +#include +#include + +// local includes + +#include "error.h" +#include "riff.h" + + +/** make a 32 bit "string-id" + + \param s a pointer to 4 chars + \return the 32 bit "string id" + \bugs It is not checked whether we really have 4 characters + + Some compilers understand constants like int id = 'ABCD'; but I + could not get it working on the gcc compiler so I had to use this + workaround. We can now use id = make_fourcc("ABCD") instead. */ + +FOURCC make_fourcc( char *s ) +{ + if ( s[ 0 ] == 0 ) + return 0; + else + return *( ( FOURCC* ) s ); +} + + +RIFFDirEntry::RIFFDirEntry() +{} + + +RIFFDirEntry::RIFFDirEntry ( FOURCC t, FOURCC n, int l, int o, int p ) : type( t ), name( n ), length( l ), offset( o ), parent( p ), written( 0 ) +{} + + +/** Creates the object without an output file. + +*/ + +RIFFFile::RIFFFile() : fd( -1 ) +{ + pthread_mutex_init( &file_mutex, NULL ); +} + + +/* Copy constructor + + Duplicate the file descriptor +*/ + +RIFFFile::RIFFFile( const RIFFFile& riff ) : fd( -1 ) +{ + if ( riff.fd != -1 ) + { + fd = dup( riff.fd ); + } + directory = riff.directory; +} + + +/** Destroys the object. + + If it has an associated opened file, close it. */ + +RIFFFile::~RIFFFile() +{ + Close(); + pthread_mutex_destroy( &file_mutex ); +} + + +RIFFFile& RIFFFile::operator=( const RIFFFile& riff ) +{ + if ( fd != riff.fd ) + { + Close(); + if ( riff.fd != -1 ) + { + fd = dup( riff.fd ); + } + directory = riff.directory; + } + return *this; +} + + +/** Creates or truncates the file. + + \param s the filename +*/ + +bool RIFFFile::Create( const char *s ) +{ + fd = open( s, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC, 00644 ); + + if ( fd == -1 ) + return false; + else + return true; +} + + +/** Opens the file read only. + + \param s the filename +*/ + +bool RIFFFile::Open( const char *s ) +{ + fd = open( s, O_RDONLY | O_NONBLOCK ); + + if ( fd == -1 ) + return false; + else + return true; +} + + +/** Destroys the object. + + If it has an associated opened file, close it. */ + +void RIFFFile::Close() +{ + if ( fd != -1 ) + { + close( fd ); + fd = -1; + } +} + + +/** Adds an entry to the list of containers. + + \param type the type of this entry + \param name the name + \param length the length of the data in the container + \param list the container in which this object is contained. + \return the ID of the newly created entry + + The topmost object is not contained in any other container. Use + the special ID RIFF_NO_PARENT to create the topmost object. */ + +int RIFFFile::AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list ) +{ + /* Put all parameters in an RIFFDirEntry object. The offset is + currently unknown. */ + + RIFFDirEntry entry( type, name, length, 0 /* offset */, list ); + + /* If the new chunk is in a list, then get the offset and size + of that list. The offset of this chunk is the end of the list + (parent_offset + parent_length) plus the size of the chunk + header. */ + + if ( list != RIFF_NO_PARENT ) + { + RIFFDirEntry parent = GetDirectoryEntry( list ); + entry.offset = parent.offset + parent.length + RIFF_HEADERSIZE; + } + + /* The list which this new chunk is a member of has now increased in + size. Get that directory entry and bump up its length by the size + of the chunk. Since that list may also be contained in another + list, walk up to the top of the tree. */ + + while ( list != RIFF_NO_PARENT ) + { + RIFFDirEntry parent = GetDirectoryEntry( list ); + parent.length += RIFF_HEADERSIZE + length; + SetDirectoryEntry( list, parent ); + list = parent.parent; + } + + directory.insert( directory.end(), entry ); + + return directory.size() - 1; +} + + +/** Modifies an entry. + + \param i the ID of the entry which is to modify + \param type the type of this entry + \param name the name + \param length the length of the data in the container + \param list the container in which this object is contained. + \note Do not change length, offset, or the parent container. + \note Do not change an empty name ("") to a name and vice versa */ + +void RIFFFile::SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list ) +{ + RIFFDirEntry entry( type, name, length, offset, list ); + + assert( i >= 0 && i < ( int ) directory.size() ); + + directory[ i ] = entry; +} + + +/** Modifies an entry. + + The entry.written flag is set to false because the contents has been modified + + \param i the ID of the entry which is to modify + \param entry the new entry + \note Do not change length, offset, or the parent container. + \note Do not change an empty name ("") to a name and vice versa */ + +void RIFFFile::SetDirectoryEntry( int i, RIFFDirEntry &entry ) +{ + assert( i >= 0 && i < ( int ) directory.size() ); + + entry.written = false; + directory[ i ] = entry; +} + + +/** Retrieves an entry. + + Gets the most important member variables. + + \param i the ID of the entry to retrieve + \param type + \param name + \param length + \param offset + \param list */ + +void RIFFFile::GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const +{ + RIFFDirEntry entry; + + assert( i >= 0 && i < ( int ) directory.size() ); + + entry = directory[ i ]; + type = entry.type; + name = entry.name; + length = entry.length; + offset = entry.offset; + list = entry.parent; +} + + +/** Retrieves an entry. + + Gets the whole RIFFDirEntry object. + + \param i the ID of the entry to retrieve + \return the entry */ + +RIFFDirEntry RIFFFile::GetDirectoryEntry( int i ) const +{ + assert( i >= 0 && i < ( int ) directory.size() ); + + return directory[ i ]; +} + + +/** Calculates the total size of the file + + \return the size the file in bytes +*/ + +off_t RIFFFile::GetFileSize( void ) const +{ + + /* If we have at least one entry, return the length field + of the FILE entry, which is the length of its contents, + which is the actual size of whatever is currently in the + AVI directory structure. + + Note that the first entry does not belong to the AVI + file. + + If we don't have any entry, the file size is zero. */ + + if ( directory.size() > 0 ) + return directory[ 0 ].length; + else + return 0; +} + + +/** prints the attributes of the entry + + \param i the ID of the entry to print +*/ + +void RIFFFile::PrintDirectoryEntry ( int i ) const +{ + RIFFDirEntry entry; + RIFFDirEntry parent; + FOURCC entry_name; + FOURCC list_name; + + /* Get all attributes of the chunk object. If it is contained + in a list, get the name of the list too (otherwise the name of + the list is blank). If the chunk object doesn´t have a name (only + LISTs and RIFFs have a name), the name is blank. */ + + entry = GetDirectoryEntry( i ); + if ( entry.parent != RIFF_NO_PARENT ) + { + parent = GetDirectoryEntry( entry.parent ); + list_name = parent.name; + } + else + { + list_name = make_fourcc( " " ); + } + if ( entry.name != 0 ) + { + entry_name = entry.name; + } + else + { + entry_name = make_fourcc( " " ); + } + + /* Print out the ascii representation of type and name, as well as + length and file offset. */ + + cout << hex << setfill( '0' ) << "type: " + << ((char *)&entry.type)[0] + << ((char *)&entry.type)[1] + << ((char *)&entry.type)[2] + << ((char *)&entry.type)[3] + << " name: " + << ((char *)&entry_name)[0] + << ((char *)&entry_name)[1] + << ((char *)&entry_name)[2] + << ((char *)&entry_name)[3] + << " length: 0x" << setw( 12 ) << entry.length + << " offset: 0x" << setw( 12 ) << entry.offset + << " list: " + << ((char *)&list_name)[0] + << ((char *)&list_name)[1] + << ((char *)&list_name)[2] + << ((char *)&list_name)[3] << dec << endl; + + /* print the content itself */ + + PrintDirectoryEntryData( entry ); +} + + +/** prints the contents of the entry + + Prints a readable representation of the contents of an index. + Override this to print out any objects you store in the RIFF file. + + \param entry the entry to print */ + +void RIFFFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const + {} + + +/** prints the contents of the whole directory + + Prints a readable representation of the contents of an index. + Override this to print out any objects you store in the RIFF file. + + \param entry the entry to print */ + +void RIFFFile::PrintDirectory() const +{ + int i; + int count = directory.size(); + + for ( i = 0; i < count; ++i ) + PrintDirectoryEntry( i ); +} + + +/** finds the index + + finds the index of a given directory entry type + + \todo inefficient if the directory has lots of items + \param type the type of the entry to find + \param n the zero-based instance of type to locate + \return the index of the found object in the directory, or -1 if not found */ + +int RIFFFile::FindDirectoryEntry ( FOURCC type, int n ) const +{ + int i, j = 0; + int count = directory.size(); + + for ( i = 0; i < count; ++i ) + if ( directory[ i ].type == type ) + { + if ( j == n ) + return i; + j++; + } + + return -1; +} + + +/** Reads all items that are contained in one list + + Read in one chunk and add it to the directory. If the chunk + happens to be of type LIST, then call ParseList recursively for + it. + + \param parent The id of the item to process +*/ + +void RIFFFile::ParseChunk( int parent ) +{ + FOURCC type; + DWORD length; + int typesize; + + /* Check whether it is a LIST. If so, let ParseList deal with it */ + + read( fd, &type, sizeof( type ) ); + if ( type == make_fourcc( "LIST" ) ) + { + typesize = -sizeof( type ); + fail_if( lseek( fd, typesize, SEEK_CUR ) == ( off_t ) - 1 ); + ParseList( parent ); + } + + /* it is a normal chunk, create a new directory entry for it */ + + else + { + fail_neg( read( fd, &length, sizeof( length ) ) ); + if ( length & 1 ) + length++; + AddDirectoryEntry( type, 0, length, parent ); + fail_if( lseek( fd, length, SEEK_CUR ) == ( off_t ) - 1 ); + } +} + + +/** Reads all items that are contained in one list + + \param parent The id of the list to process + +*/ + +void RIFFFile::ParseList( int parent ) +{ + FOURCC type; + FOURCC name; + int list; + DWORD length; + off_t pos; + off_t listEnd; + + /* Read in the chunk header (type and length). */ + fail_neg( read( fd, &type, sizeof( type ) ) ); + fail_neg( read( fd, &length, sizeof( length ) ) ); + + if ( length & 1 ) + length++; + + /* The contents of the list starts here. Obtain its offset. The list + name (4 bytes) is already part of the contents). */ + + pos = lseek( fd, 0, SEEK_CUR ); + fail_if( pos == ( off_t ) - 1 ); + fail_neg( read( fd, &name, sizeof( name ) ) ); + + /* Add an entry for this list. */ + + list = AddDirectoryEntry( type, name, sizeof( name ), parent ); + + /* Read in any chunks contained in this list. This list is the + parent for all chunks it contains. */ + + listEnd = pos + length; + while ( pos < listEnd ) + { + ParseChunk( list ); + pos = lseek( fd, 0, SEEK_CUR ); + fail_if( pos == ( off_t ) - 1 ); + } +} + + +/** Reads the directory structure of the whole RIFF file + +*/ + +void RIFFFile::ParseRIFF( void ) +{ + FOURCC type; + DWORD length; + off_t filesize; + off_t pos; + int container = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT ); + + pos = lseek( fd, 0, SEEK_SET ); + + /* calculate file size from RIFF header instead from physical file. */ + + while ( ( read( fd, &type, sizeof( type ) ) > 0 ) && + ( read( fd, &length, sizeof( length ) ) > 0 ) && + ( type == make_fourcc( "RIFF" ) ) ) + { + + filesize += length + RIFF_HEADERSIZE; + + fail_if( lseek( fd, pos, SEEK_SET ) == ( off_t ) - 1 ); + ParseList( container ); + pos = lseek( fd, 0, SEEK_CUR ); + fail_if( pos == ( off_t ) - 1 ); + } +} + + +/** Reads one item including its contents from the RIFF file + + \param chunk_index The index of the item to write + \param data A pointer to the data + +*/ + +void RIFFFile::ReadChunk( int chunk_index, void *data ) +{ + RIFFDirEntry entry; + + entry = GetDirectoryEntry( chunk_index ); + pthread_mutex_lock( &file_mutex ); + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, data, entry.length ) ); + pthread_mutex_unlock( &file_mutex ); +} + + +/** Writes one item including its contents to the RIFF file + + \param chunk_index The index of the item to write + \param data A pointer to the data + +*/ + +void RIFFFile::WriteChunk( int chunk_index, const void *data ) +{ + RIFFDirEntry entry; + + entry = GetDirectoryEntry( chunk_index ); + pthread_mutex_lock( &file_mutex ); + fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) ); + DWORD length = entry.length; + fail_neg( write( fd, &length, sizeof( length ) ) ); + fail_neg( write( fd, data, entry.length ) ); + pthread_mutex_unlock( &file_mutex ); + + /* Remember that this entry already has been written. */ + + directory[ chunk_index ].written = true; +} + + +/** Writes out the directory structure + + For all items in the directory list that have not been written + yet, it seeks to the file position where that item should be + stored and writes the type and length field. If the item has a + name, it will also write the name field. + + \note It does not write the contents of any item. Use WriteChunk to do that. */ + +void RIFFFile::WriteRIFF( void ) +{ + int i; + RIFFDirEntry entry; + int count = directory.size(); + + /* Start at the second entry (RIFF), since the first entry (FILE) + is needed only for internal purposes and is not written to the + file. */ + + for ( i = 1; i < count; ++i ) + { + + /* Only deal with entries that haven´t been written */ + + entry = GetDirectoryEntry( i ); + if ( entry.written == false ) + { + + /* A chunk entry consist of its type and length, a list + entry has an additional name. Look up the entry, seek + to the start of the header, which is at the offset of + the data start minus the header size and write out the + items. */ + + fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ) ; + fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) ); + DWORD length = entry.length; + fail_neg( write( fd, &length, sizeof( length ) ) ); + + /* If it has a name, it is a list. Write out the extra name + field. */ + + if ( entry.name != 0 ) + { + fail_neg( write( fd, &entry.name, sizeof( entry.name ) ) ); + } + + /* Remember that this entry already has been written. */ + + directory[ i ].written = true; + } + } +} diff --git a/src/modules/kino/riff.h b/src/modules/kino/riff.h new file mode 100644 index 00000000..e7a21464 --- /dev/null +++ b/src/modules/kino/riff.h @@ -0,0 +1,140 @@ +/* +* riff.h library for RIFF file format i/o +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* 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. +* +* Tag: $Name$ +* +* Change log: +* +* $Log$ +* Revision 1.1 2005/04/15 14:28:26 lilo_booter +* Initial version +* +* Revision 1.14 2005/04/01 23:43:10 ddennedy +* apply endian fixes from Daniel Kobras +* +* Revision 1.13 2004/10/11 01:37:11 ddennedy +* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script +* +* Revision 1.12 2004/01/06 22:53:42 ddennedy +* metadata editing tweaks and bugfixes, new ui elements in preparation for publish functions +* +* Revision 1.11 2003/11/25 23:01:25 ddennedy +* cleanup and a few bugfixes +* +* Revision 1.10 2003/10/21 16:34:34 ddennedy +* GNOME2 port phase 1: initial checkin +* +* Revision 1.8.4.1 2002/11/25 04:48:31 ddennedy +* bugfix to report errors when loading files +* +* Revision 1.8 2002/04/21 06:36:40 ddennedy +* kindler avc and 1394 bus reset support in catpure page, honor max file size +* +* Revision 1.7 2002/04/09 06:53:42 ddennedy +* cleanup, new libdv 0.9.5, large AVI, dnd storyboard +* +* Revision 1.3 2002/03/25 21:34:25 arne +* Support for large (64 bit) files mostly completed +* +* Revision 1.2 2002/03/04 19:22:43 arne +* updated to latest Kino avi code +* +* Revision 1.1.1.1 2002/03/03 19:08:08 arne +* import of version 1.01 +* +*/ + +#ifndef _RIFF_H +#define _RIFF_H 1 + +#include +using std::vector; + +#include + +#include "endian_types.h" + +#define QUADWORD int64_le_t +#define DWORD int32_le_t +#define LONG u_int32_le_t +#define WORD int16_le_t +#define BYTE u_int8_le_t +#define FOURCC u_int32_t // No endian conversion needed. + +#define RIFF_NO_PARENT (-1) +#define RIFF_LISTSIZE (4) +#define RIFF_HEADERSIZE (8) + +#ifdef __cplusplus +extern "C" +{ + FOURCC make_fourcc( char * s ); +} +#endif + +class RIFFDirEntry +{ +public: + FOURCC type; + FOURCC name; + off_t length; + off_t offset; + int parent; + int written; + + RIFFDirEntry(); + RIFFDirEntry( FOURCC t, FOURCC n, int l, int o, int p ); +}; + + +class RIFFFile +{ +public: + RIFFFile(); + RIFFFile( const RIFFFile& ); + virtual ~RIFFFile(); + RIFFFile& operator=( const RIFFFile& ); + + virtual bool Open( const char *s ); + virtual bool Create( const char *s ); + virtual void Close(); + virtual int AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list ); + virtual void SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list ); + virtual void SetDirectoryEntry( int i, RIFFDirEntry &entry ); + virtual void GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const; + virtual RIFFDirEntry GetDirectoryEntry( int i ) const; + virtual off_t GetFileSize( void ) const; + virtual void PrintDirectoryEntry( int i ) const; + virtual void PrintDirectoryEntryData( const RIFFDirEntry &entry ) const; + virtual void PrintDirectory( void ) const; + virtual int FindDirectoryEntry( FOURCC type, int n = 0 ) const; + virtual void ParseChunk( int parent ); + virtual void ParseList( int parent ); + virtual void ParseRIFF( void ); + virtual void ReadChunk( int chunk_index, void *data ); + virtual void WriteChunk( int chunk_index, const void *data ); + virtual void WriteRIFF( void ); + +protected: + int fd; + pthread_mutex_t file_mutex; + +private: + vector directory; +}; +#endif