AutoOggSeekTable.cpp

Go to the documentation of this file.
00001 //===========================================================================
00002 //Copyright (C) 2003, 2004 Zentaro Kavanagh
00003 //
00004 //Redistribution and use in source and binary forms, with or without
00005 //modification, are permitted provided that the following conditions
00006 //are met:
00007 //
00008 //- Redistributions of source code must retain the above copyright
00009 //  notice, this list of conditions and the following disclaimer.
00010 //
00011 //- Redistributions in binary form must reproduce the above copyright
00012 //  notice, this list of conditions and the following disclaimer in the
00013 //  documentation and/or other materials provided with the distribution.
00014 //
00015 //- Neither the name of Zentaro Kavanagh nor the names of contributors 
00016 //  may be used to endorse or promote products derived from this software 
00017 //  without specific prior written permission.
00018 //
00019 //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020 //``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021 //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
00022 //PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
00023 //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00024 //EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00025 //PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00026 //PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00027 //LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00028 //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00029 //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 //===========================================================================
00031 
00032 #include "stdafx.h"
00033 #include <libOOOggSeek/AutoOggSeekTable.h>
00034 
00035 AutoOggSeekTable::AutoOggSeekTable(string inFileName)
00036         :       mFilePos(0)
00037         ,       mLastSeekTime(0)
00038         ,       mPacketCount(0)
00039         ,       mSampleRate(0)
00040         ,       mFileDuration(0)
00041         ,       mNumHeaders(0)
00042         ,       mSerialNoToTrack(LINT_MAX)
00043         ,       isTheora(false)
00044         ,       isFLAC(false)
00045         ,       isOggFLAC_1_0(false)
00046         ,       mFoundStreamInfo(false)
00047         ,       mGranulePosShift(0)
00048         ,       mLastIsSeekable(false)
00049 
00050 {
00051 
00052         mFileName = inFileName;
00053         mOggDemux = new OggDataBuffer();                        //Deleted in destructor.
00054         mOggDemux->registerVirtualCallback(this);
00055         //debugLog.open("G:\\logs\\seektable.log", ios_base::out);
00056         //debugLog<<"Constructing seek table for "<<inFileName<<endl;
00057 }
00058 
00059 AutoOggSeekTable::~AutoOggSeekTable(void)
00060 {
00061         //debugLog<<"Closing file (Constructor)..."<<endl;
00062         //debugLog.close();
00063         mFile.close();
00064         delete mOggDemux;
00065 }
00066 
00067 bool AutoOggSeekTable::acceptOggPage(OggPage* inOggPage) {                      //Correctly deletes page.
00068         
00069 
00070         //TODO ::: Some of this could be shared from other places.
00071         if (!mFoundStreamInfo) {
00072                 if (strncmp((const char*)inOggPage->getPacket(0)->packetData(), "\001vorbis", 7) == 0) {
00073                         mSampleRate = iLE_Math::charArrToULong(inOggPage->getPacket(0)->packetData() + 12);
00074                         mNumHeaders = 3;
00075                         mSerialNoToTrack = inOggPage->header()->StreamSerialNo();
00076                         mFoundStreamInfo = true;
00077                 } else if (strncmp((const char*)inOggPage->getPacket(0)->packetData(), "Speex   ", 8) == 0) {
00078                         mSampleRate = iLE_Math::charArrToULong(inOggPage->getPacket(0)->packetData() + 36);
00079                         mNumHeaders = 2;
00080                         mSerialNoToTrack = inOggPage->header()->StreamSerialNo();
00081                         mFoundStreamInfo = true;
00082                 } else if ((strncmp((char*)inOggPage->getPacket(0)->packetData(), "\200theora", 7)) == 0){
00083                         //FIX ::: Dunno what this is... do something better than this later !!
00084                         //mEnabled = false;
00085                         //mPacketCount == 0;
00086                         isTheora = true;
00087                         mSerialNoToTrack = inOggPage->header()->StreamSerialNo();
00088                         mGranulePosShift = (((inOggPage->getPacket(0)->packetData()[40]) % 4) << 3) + ((inOggPage->getPacket(0)->packetData()[41]) >> 5);
00089                         mSampleRate = iBE_Math::charArrToULong(inOggPage->getPacket(0)->packetData() + 22) / iBE_Math::charArrToULong(inOggPage->getPacket(0)->packetData() + 26);
00090                         mNumHeaders = 3;
00091                         mFoundStreamInfo = true;
00092                         
00093                         //Need denominators
00094                         //mTheoraFormatBlock->frameRateDenominator = FLACMath::charArrToULong(locIdentHeader + 26);
00095                 } else if ((strncmp((char*)inOggPage->getPacket(0)->packetData(),  "fLaC", 4) == 0)) {
00096                         //mPacketCount--;
00097                         mNumHeaders = 1;
00098                         mSerialNoToTrack = inOggPage->header()->StreamSerialNo();
00099                         isFLAC = true;
00100                 } else if (isFLAC && (mSerialNoToTrack == inOggPage->header()->StreamSerialNo()) ) {
00101                         //Loop any other packets
00102 
00103                         const int FLAC_LAST_HEADERS_FLAG = 128;
00104                         const int FLAC_HEADER_MASK = 127;
00105                         const int FLAC_STREAM_INFO_ID = 0;
00106                         
00107                         //Note ::: Secondary condition in for statement.
00108             for (unsigned int i = 0; i < inOggPage->numPackets(), !mFoundStreamInfo; i++) {
00109                                 mNumHeaders++;
00110                                 if ((inOggPage->getPacket(i)->packetData()[0] & FLAC_HEADER_MASK) == FLAC_STREAM_INFO_ID) {
00111                     //Catch the stream info packet.
00112                     mSampleRate = iBE_Math::charArrToULong(inOggPage->getPacket(0)->packetData() + 14) >> 12;
00113                                         //mFoundStreamInfo = true;
00114                                 }
00115                                 if ((inOggPage->getPacket(i)->packetData()[0] & FLAC_LAST_HEADERS_FLAG)) {
00116                                         mFoundStreamInfo = true;
00117                                 }
00118                         }
00119                 } else if ((strncmp((char*)inOggPage->getPacket(0)->packetData(),  "\177FLAC", 5) == 0)) {
00120                         //debugLog<<"Identified new flac..."<<endl;
00121                         //mPacketCount--;
00122                         //POTENTIAL BUG::: Only looks at low order byte
00123                         mNumHeaders = inOggPage->getPacket(0)->packetData()[8] + 1;
00124                         //debugLog<<"Header says there are this many headers "<<mNumHeaders<<endl;
00125                         mSerialNoToTrack = inOggPage->header()->StreamSerialNo();
00126                         if (mNumHeaders == 0) {
00127                                 //Variable number of headers... need to pick it up again...
00128                                 //debugLog<<"Variable number of headers... 1 so far..."<<endl;
00129                                 mNumHeaders = 1;
00130                 isOggFLAC_1_0 = true;
00131                         } else {
00132                                 //debugLog<<"Fixed number of headers..."<<endl;
00133                                 mFoundStreamInfo = true;
00134                         }
00135                         mSampleRate = iBE_Math::charArrToULong(inOggPage->getPacket(0)->packetData() + 27) >> 12;
00136                 } else if (isOggFLAC_1_0 && (mSerialNoToTrack == inOggPage->header()->StreamSerialNo()) ) {
00137                         //Loop any other packets
00138 
00139                         const int FLAC_LAST_HEADERS_FLAG = 128;
00140                         // const int FLAC_HEADER_MASK = 127;  // Unused
00141                         // const int FLAC_STREAM_INFO_ID = 0;  // Unused
00142                         
00143                         //Note ::: Secondary condition in for statement.
00144             for (unsigned int i = 0; i < inOggPage->numPackets(), !mFoundStreamInfo; i++) {
00145                                 mNumHeaders++;
00146 
00147                                 //Don't need this, we already got this data... we're just counting headers.
00148                                 //if ((inOggPage->getPacket(i)->packetData()[0] & FLAC_HEADER_MASK) == FLAC_STREAM_INFO_ID) {
00149     //                //Catch the stream info packet.
00150     //                mSampleRate = iBE_Math::charArrToULong(inOggPage->getPacket(0)->packetData() + 14) >> 12;
00151                                 //}
00152                                 if ((inOggPage->getPacket(i)->packetData()[0] & FLAC_LAST_HEADERS_FLAG)) {
00153                                         mFoundStreamInfo = true;
00154                                 }
00155                         }
00156                         
00157                 } else if ((strncmp((char*)inOggPage->getPacket(0)->packetData(), "\001video\000\000\000", 9)) == 0) {
00158                         //FFDSHOW
00159                         LOOG_INT64 locTimePerBlock = iLE_Math::CharArrToInt64(inOggPage->getPacket(0)->packetData() + 17);
00160                         LOOG_INT64 locSamplesPerBlock = iLE_Math::CharArrToInt64(inOggPage->getPacket(0)->packetData() + 25);
00161 
00162                         //TODO::: The division is the wrong way round !!
00163                         mSampleRate = (unsigned long) ( (10000000 / locTimePerBlock) * locSamplesPerBlock );
00164                         mFoundStreamInfo = true;
00165                         mSerialNoToTrack = inOggPage->header()->StreamSerialNo();
00166                         mNumHeaders = 1;
00167 
00168                 } else {
00169                         mFoundStreamInfo = true;                //Why do this ?
00170                         mEnabled = false;
00171                         mSampleRate = 1;
00172                         
00173                 }
00174         }
00175 
00176         if (mSerialNoToTrack == inOggPage->header()->StreamSerialNo()) {
00177                 mPacketCount += inOggPage->numPackets();
00178         }
00179 
00180 
00181         if ((mFoundStreamInfo) && (mSerialNoToTrack == inOggPage->header()->StreamSerialNo()) && (inOggPage->header()->GranulePos() != -1)) {
00182                 //if ((mPacketCount > 3) && (mLastIsSeekable == true)) {
00183                 //debugLog<<"Stream headers complete..."<<endl;
00184                 //debugLog<<"Num Headers = "<<mNumHeaders<<endl;
00185                 //debugLog<<"Packet COunt = "<<mPacketCount<<endl;
00186                 if ((mPacketCount > mNumHeaders) && ((inOggPage->header()->HeaderFlags() & 1) != 1)) {
00187                         //debugLog<<"Adding seek point Time = "<<mLastSeekTime<<"  --  File pos = "<<mFilePos<<endl;
00188                         addSeekPoint(mLastSeekTime, mFilePos);
00189                         
00190                 }
00191 
00192                 mLastIsSeekable = true;
00193                 
00194                 if (isTheora) {
00195                         unsigned long locMod = (unsigned long)pow((double) 2, (double) mGranulePosShift);
00196                         unsigned long locInterFrameNo = (unsigned long) ( (inOggPage->header()->GranulePos()) % locMod );
00197                         
00198                         //if (locInterFrameNo == 0) {
00199                         //      mLastIsSeekable = true;
00200                         //} else {
00201                         //      mLastIsSeekable = false;
00202                         //}
00203                         mLastSeekTime = ((((inOggPage->header()->GranulePos()) >> mGranulePosShift) + locInterFrameNo) * DS_UNITS) / mSampleRate;
00204                 } else {
00205                         mLastSeekTime = ((inOggPage->header()->GranulePos()) * DS_UNITS) / mSampleRate;
00206                         //stDebug<<"Last Seek Time : "<<mLastSeekTime;
00207                 }
00208                 if (((inOggPage->header()->HeaderFlags() & 1) == 1)) {
00209                         //stDebug <<"    NOT SEEKABLE";
00210                         mLastIsSeekable = false;
00211                 }
00212                 //stDebug<<endl;
00213                 mFileDuration = mLastSeekTime;
00214                 
00215         }
00216         mFilePos += inOggPage->pageSize();
00217         //stDebug<<"File Pos : "<<mFilePos<<endl;
00218 
00219         //Memory leak ::: Need to delete the page.
00220         delete inOggPage;
00221 
00222         return true;
00223 }
00224 unsigned long AutoOggSeekTable::serialisedSize() {
00225         // TODO: This really should return a size_t (in fact, all our file-size-related variables should really be
00226         // a size_t), but let's just use ye olde unsigned long for now ...
00227         return (unsigned long) mSeekMap.size() * 12;
00228         
00229 }
00230 bool AutoOggSeekTable::serialiseInto(unsigned char* inBuff, unsigned long inBuffSize) {
00231         if (inBuffSize >= serialisedSize()) {
00232                 unsigned long locUpto = 0;
00233                 for (tSeekMap::const_iterator i = mSeekMap.begin(); i != mSeekMap.end(); i++) {
00234 
00235                         //Time is .first
00236                         iLE_Math::Int64ToCharArr((*i).first, inBuff + locUpto);
00237                         locUpto += 8;
00238 
00239                         //Byte offset is .second
00240                         iLE_Math::ULongToCharArr((*i).second, inBuff + locUpto);
00241                         locUpto += 4;
00242                 }
00243                 return true;
00244         } else {
00245                 return false;
00246         }
00247 }
00248 
00249 bool AutoOggSeekTable::serialiseInto(const string inSeekTableFilename)
00250 {
00251         unsigned long locSerialisedSeekTableSize = serialisedSize();
00252         unsigned char *locBuffer = new unsigned char[locSerialisedSeekTableSize];
00253 
00254         if (serialiseInto(locBuffer, locSerialisedSeekTableSize)) {
00255                 fstream locOutputFile;
00256 
00257                 locOutputFile.open(inSeekTableFilename.c_str(), ios_base::out | ios_base::binary);
00258                 locOutputFile.write((char*)locBuffer, locSerialisedSeekTableSize);
00259                 locOutputFile.close();
00260         } else {
00261                 delete [] locBuffer;
00262                 return false;
00263         }
00264 
00265         delete [] locBuffer;
00266         return true;
00267 }
00268 
00269 
00270 LOOG_INT64 AutoOggSeekTable::fileDuration()
00271 {
00272         return mFileDuration;
00273 }
00274 
00275 bool AutoOggSeekTable::buildTable()
00276 {
00277         //HACK::: To ensure we don't try and build a table on the network file.
00278         //debugLog<<"Anx Build table : "<<mFileName<<endl;
00279         if (mFileName.find("http") != 0) {
00280                 
00281                 mSeekMap.clear();
00282                 addSeekPoint(0, 0);
00283                 //debugLog<<"Opening file... "<<endl;
00284                 mFile.open(mFileName.c_str(), ios_base::in | ios_base::binary);
00285                 const unsigned long BUFF_SIZE = 4096;
00286                 unsigned char* locBuff = new unsigned char[BUFF_SIZE];          //Deleted this function.
00287                 while (!mFile.eof()) {
00288                         mFile.read((char*)locBuff, BUFF_SIZE);
00289                         mOggDemux->feed((const unsigned char*)locBuff, mFile.gcount());
00290                 }
00291                 delete[] locBuff;
00292                 //debugLog<<"Closing File..."<<endl;
00293                 mFile.close();
00294                 
00295         } else {
00296                 //debugLog<<"Not SEEKABLE"<<endl;
00297                 mEnabled = false;
00298                 mSampleRate = 1;
00299         }
00300 
00301         return true;
00302 }
00303 
00304 bool AutoOggSeekTable::buildTableFromBuffer(const unsigned char *inBuffer, const unsigned long inBufferSize)
00305 {
00306         for (const unsigned char *locBufferPosition = inBuffer; locBufferPosition < inBuffer + inBufferSize; ) {
00307                 LOOG_INT64 locTimePoint = iLE_Math::CharArrToInt64(locBufferPosition);
00308                 locBufferPosition += 8;
00309 
00310                 unsigned long locBytePosition = iLE_Math::charArrToULong(locBufferPosition);
00311                 locBufferPosition += 4;
00312 
00313                 addSeekPoint(locTimePoint, locBytePosition);
00314         }
00315 
00316         return true;
00317 }
00318 
00324 bool AutoOggSeekTable::buildTableFromFile(const string inCachedSeekTableFilename)
00325 {
00326         LOOG_INT64 locTimePoint;
00327         unsigned long locBytePosition;
00328 
00329         fstream locSeekFile;
00330         locSeekFile.open(inCachedSeekTableFilename.c_str(), ios_base::in | ios_base::binary);
00331 
00332         // Look ma, we got us zergling-size buffer
00333         unsigned char* locBuffer = new unsigned char[16];
00334 
00335         while (!locSeekFile.eof()) {
00336                 locSeekFile.read((char*)locBuffer, 8);
00337                 if (locSeekFile.gcount() == 8) {
00338                         locTimePoint = iLE_Math::CharArrToInt64(locBuffer);
00339 
00340                         locSeekFile.read((char*)locBuffer, 4);
00341                         if (locSeekFile.gcount() == 4) {
00342                                 locBytePosition = iLE_Math::charArrToULong(locBuffer);
00343                         } else {
00344                                 delete[] locBuffer;
00345                                 return false;
00346                         }
00347 
00348                         addSeekPoint(locTimePoint, locBytePosition);
00349                 } else {
00350                         delete [] locBuffer;
00351                         return false;
00352                 }
00353         }
00354 
00355         delete [] locBuffer;
00356 
00357         return true;
00358 }
00359 

Generated on Tue Feb 15 14:54:21 2005 for oggdsf by  doxygen 1.3.9