00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
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();
00054 mOggDemux->registerVirtualCallback(this);
00055
00056
00057 }
00058
00059 AutoOggSeekTable::~AutoOggSeekTable(void)
00060 {
00061
00062
00063 mFile.close();
00064 delete mOggDemux;
00065 }
00066
00067 bool AutoOggSeekTable::acceptOggPage(OggPage* inOggPage) {
00068
00069
00070
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
00084
00085
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
00094
00095 } else if ((strncmp((char*)inOggPage->getPacket(0)->packetData(), "fLaC", 4) == 0)) {
00096
00097 mNumHeaders = 1;
00098 mSerialNoToTrack = inOggPage->header()->StreamSerialNo();
00099 isFLAC = true;
00100 } else if (isFLAC && (mSerialNoToTrack == inOggPage->header()->StreamSerialNo()) ) {
00101
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
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
00112 mSampleRate = iBE_Math::charArrToULong(inOggPage->getPacket(0)->packetData() + 14) >> 12;
00113
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
00121
00122
00123 mNumHeaders = inOggPage->getPacket(0)->packetData()[8] + 1;
00124
00125 mSerialNoToTrack = inOggPage->header()->StreamSerialNo();
00126 if (mNumHeaders == 0) {
00127
00128
00129 mNumHeaders = 1;
00130 isOggFLAC_1_0 = true;
00131 } else {
00132
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
00138
00139 const int FLAC_LAST_HEADERS_FLAG = 128;
00140
00141
00142
00143
00144 for (unsigned int i = 0; i < inOggPage->numPackets(), !mFoundStreamInfo; i++) {
00145 mNumHeaders++;
00146
00147
00148
00149
00150
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
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
00163 mSampleRate = (unsigned long) ( (10000000 / locTimePerBlock) * locSamplesPerBlock );
00164 mFoundStreamInfo = true;
00165 mSerialNoToTrack = inOggPage->header()->StreamSerialNo();
00166 mNumHeaders = 1;
00167
00168 } else {
00169 mFoundStreamInfo = true;
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
00183
00184
00185
00186 if ((mPacketCount > mNumHeaders) && ((inOggPage->header()->HeaderFlags() & 1) != 1)) {
00187
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
00199
00200
00201
00202
00203 mLastSeekTime = ((((inOggPage->header()->GranulePos()) >> mGranulePosShift) + locInterFrameNo) * DS_UNITS) / mSampleRate;
00204 } else {
00205 mLastSeekTime = ((inOggPage->header()->GranulePos()) * DS_UNITS) / mSampleRate;
00206
00207 }
00208 if (((inOggPage->header()->HeaderFlags() & 1) == 1)) {
00209
00210 mLastIsSeekable = false;
00211 }
00212
00213 mFileDuration = mLastSeekTime;
00214
00215 }
00216 mFilePos += inOggPage->pageSize();
00217
00218
00219
00220 delete inOggPage;
00221
00222 return true;
00223 }
00224 unsigned long AutoOggSeekTable::serialisedSize() {
00225
00226
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
00236 iLE_Math::Int64ToCharArr((*i).first, inBuff + locUpto);
00237 locUpto += 8;
00238
00239
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
00278
00279 if (mFileName.find("http") != 0) {
00280
00281 mSeekMap.clear();
00282 addSeekPoint(0, 0);
00283
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];
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
00293 mFile.close();
00294
00295 } else {
00296
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
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