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 <libOOOgg/OggDataBuffer.h> 00034 00035 00036 //LEAK CHECKED - 2004/10/17 - OK. 00037 //LEAK FOUND - 2004/11/29 - acceptOggPage 00038 OggDataBuffer::OggDataBuffer(void) 00039 : mBuffer(NULL) 00040 , mPrevGranPos(0) 00041 , pendingPage(NULL) 00042 , mState(AWAITING_BASE_HEADER) 00043 , mNumBytesNeeded(OggPageHeader::OGG_BASE_HEADER_SIZE) 00044 { 00045 mBuffer = new CircularBuffer(MAX_OGG_PAGE_SIZE); //Deleted in destructor 00046 //debugLog.open("G:\\logs\\OggDataBuffer.log", ios_base::out); 00047 } 00048 00049 //Debug only 00050 //OggDataBuffer::OggDataBuffer(bool x) 00051 // : mBuffer(NULL) 00052 // , mPrevGranPos(0) 00053 //{ 00054 // mBuffer = new CircularBuffer(MAX_OGG_PAGE_SIZE); //Deleted in destructor 00055 // 00056 // //debugLog.open("G:\\logs\\OggDataBufferSeek.log", ios_base::out); 00057 // pendingPage = NULL; 00058 // mState = AWAITING_BASE_HEADER; 00059 // mNumBytesNeeded = OggPageHeader::OGG_BASE_HEADER_SIZE; 00060 //} 00061 // 00062 00063 OggDataBuffer::~OggDataBuffer(void) 00064 { 00065 delete mBuffer; 00066 delete pendingPage; 00067 //debugLog.close(); 00068 00069 } 00070 00071 00080 bool OggDataBuffer::registerStaticCallback(fPageCallback inPageCallback, void* inUserData) 00081 { 00082 //Holds the static callback and nulls the virtual one. 00083 mStaticCallback = inPageCallback; 00084 mStaticCallbackUserData = (void *) inUserData; 00085 mVirtualCallback = NULL; 00086 00087 return true; 00088 } 00089 00090 //bool OggDataBuffer::registerSerialNo(SerialNoRego* inSerialRego) { 00091 // if (inSerialRego != NULL) { 00092 // mSerialNoCallList.push_back(inSerialRego); 00093 // return true; 00094 // } else { 00095 // return false; 00096 // } 00097 //} 00098 00099 00105 bool OggDataBuffer::registerVirtualCallback(IOggCallback* inPageCallback) { 00106 //Holds the virtual callback and nulls the static one. 00107 mVirtualCallback = inPageCallback; 00108 mStaticCallback = NULL; 00109 00110 return true; 00111 00112 } 00113 00114 00115 unsigned long OggDataBuffer::numBytesAvail() { 00116 //Returns how many bytes are available in the buffer 00117 unsigned long locBytesAvail = mBuffer->numBytesAvail(); 00118 00119 //debugLog<<"Bytes avail = "<<locBytesAvail<<endl; 00120 return locBytesAvail; 00121 } 00122 00123 OggDataBuffer::eState OggDataBuffer::state() { 00124 //returns the state of the stream 00125 return mState; 00126 } 00127 //This function accepts the responsibility for the incoming page. 00128 OggDataBuffer::eDispatchResult OggDataBuffer::dispatch(OggPage* inOggPage) { 00129 //TODO::: Who owns this pointer inOggPage ? 00130 00131 //debugLog<<"Dispatching page..."<<endl<<endl; 00132 00133 //Fire off the oggpage to whoever is registered to get it 00134 if (mVirtualCallback != NULL) { 00135 if (mVirtualCallback->acceptOggPage(inOggPage) == true) { //Page given away, never used again. 00136 return DISPATCH_OK; 00137 } else { 00138 return DISPATCH_FALSE; 00139 } 00140 } else if (mStaticCallback != NULL) { 00141 if (mStaticCallback(inOggPage, mStaticCallbackUserData) == true) { 00142 return DISPATCH_OK; 00143 } else { 00144 return DISPATCH_FALSE; 00145 } 00146 } 00147 00148 //Delete the page... if the called functions wanted a copy they should have taken one for themsselves. 00149 //Not any more acceptOggPage takes responsibility for the memory you pass into it. See IOggCallback.h 00150 //delete inOggPage; 00151 pendingPage = NULL; 00152 return DISPATCH_NO_CALLBACK; 00153 } 00154 00155 OggDataBuffer::eFeedResult OggDataBuffer::feed(const unsigned char* inData, unsigned long inNumBytes) { 00156 if (inNumBytes != 0) { 00157 if (inData != NULL) { 00158 //Buffer is not null and there is at least 1 byte of data. 00159 00160 //debugLog<<"Fed "<<inNumBytes<<" bytes..."<<endl; 00161 unsigned long locNumWritten = mBuffer->write(inData, inNumBytes); 00162 00163 if (locNumWritten < inNumBytes) { 00164 //TODO::: What does happen in this case. 00165 00166 //Handle this case... you lose data. 00167 //Buffer is full 00168 00169 //debugLog<<"Feed : Could count feed in " <<inNumBytes<<" bytes"<<endl 00170 // <<"Feed : ** "<<mBuffer->numBytesAvail()<<" avail, "<<mBuffer->spaceLeft()<<" space left."<<endl; 00171 00172 locNumWritten = locNumWritten; 00173 } 00174 return (eFeedResult)processBuffer(); 00175 } else { 00176 //Numbytes not equal to zero but inData pointer is NULL 00177 00178 //debugLog<<"Feed : Fed NULL Pointer"<<endl; 00179 return FEED_NULL_POINTER; 00180 } 00181 } else { 00182 //numbytes was zero... we do nothing and it's not an error. 00183 00184 //debugLog<<"Feed : Fed *zero* bytes... Not an error, do nothing, return ok."<<endl; 00185 return FEED_OK; 00186 } 00187 00188 00189 } 00190 OggDataBuffer::eProcessResult OggDataBuffer::processBaseHeader() 00191 { 00192 //debugLog<<"Processing base header..."<<endl; 00193 00194 //Delete the previous page 00195 delete pendingPage; 00196 00197 //Make a fresh ogg page 00198 pendingPage = new OggPage; //Either deleted in destructor, or given away by virtue of dispatch method. 00199 00200 //Make a local buffer for the header 00201 unsigned char* locBuff = new unsigned char[OggPageHeader::OGG_BASE_HEADER_SIZE]; //deleted before this function returns 00202 00203 //debugLog<<"ProcessBaseHeader : Reading from stream..."<<endl; 00204 00205 //STREAM ACCESS::: Read 00206 unsigned long locNumRead = mBuffer->read(locBuff, OggPageHeader::OGG_BASE_HEADER_SIZE); 00207 00208 if (locNumRead < OggPageHeader::OGG_BASE_HEADER_SIZE) { 00209 //TODO::: Handle this case... we read less than we expected. 00210 00211 //debugLog<<"ProcessBaseHeader : ###### Read was short."<<endl; 00212 //debugLog<<"ProcessBaseHeader : ** "<<mBuffer->numBytesAvail()<<" avail, "<<mBuffer->spaceLeft()<<" space left."<<endl; 00213 locNumRead = locNumRead; 00214 } 00215 00216 bool locRetVal = pendingPage->header()->setBaseHeader((unsigned char*)locBuff); //Views pointer only. 00217 if (locRetVal == false) { 00218 delete[] locBuff; 00219 return PROCESS_FAILED_TO_SET_HEADER; 00220 } 00221 00222 //Set the number of bytes we want for next time 00223 mNumBytesNeeded = pendingPage->header()->NumPageSegments(); 00224 00225 //debugLog<<"Setting state to AWAITING_SEG_TABLE"<<endl; 00226 //Change the state. 00227 mState = AWAITING_SEG_TABLE; 00228 00229 delete[] locBuff; 00230 //debugLog<<"Bytes needed for seg table = "<<mNumBytesNeeded<<endl; 00231 return PROCESS_OK; 00232 } 00233 OggDataBuffer::eProcessResult OggDataBuffer::processSegTable() 00234 { 00236 // Gets the number segments from from the page header, reads in that much data, 00237 // 00238 // 00239 // 00240 //Assumes a valid pending page, with numPagesegments set in the header already. 00241 //creates a chunk of memory size numpagesegments and stores it,. 00242 00243 //debugLog<<"Processing Segment Table..."<<endl; 00244 00245 //TODAY::: What happens when numpage segments is zero. 00246 00247 //Save a local copy of the number of page segments - Get this from the already set header. 00248 unsigned char locNumSegs = pendingPage->header()->NumPageSegments(); 00249 00250 //debugLog<<"Num segments = "<<(int)locNumSegs<<endl; 00251 00252 //Make a local buffer the size of the segment table. 0 - 255 00253 unsigned char* locBuff = new unsigned char[locNumSegs]; //Given to setSegmentTable. Not deleted here. 00254 00256 00257 //Read the segment table from the buffer to locBuff 00258 unsigned long locNumRead = mBuffer->read(locBuff, (unsigned long)locNumSegs); 00259 00260 if (locNumRead < locNumSegs) { 00261 //TODO::: Handle this case 00262 00263 //debugLog<<"ProcessSegTable : ##### Short read"<<endl; 00264 //debugLog<<"ProcessSegTable : ** "<<mBuffer->numBytesAvail()<<" avail, "<<mBuffer->spaceLeft()<<" space left."<<endl; 00265 00266 } 00267 00268 00269 //Make a new segment table from the bufferd data. 00270 pendingPage->header()->setSegmentTable(locBuff); //This function accepts responsibility for the pointer. 00271 locBuff = NULL; 00272 00273 //Set the number of bytes we want for next time - which is the size of the page data. 00274 mNumBytesNeeded = pendingPage->header()->calculateDataSize(); 00275 00276 //debugLog<<"Num bytes needed for data = "<< mNumBytesNeeded<<endl; 00277 //debugLog<<"Transition to AWAITING_DATA"<<endl; 00278 00279 mState = AWAITING_DATA; 00280 return PROCESS_OK; 00281 00282 } 00283 00284 OggDataBuffer::eProcessResult OggDataBuffer::processDataSegment() 00285 { 00286 //unsigned long locPageDataSize = pendingPage->header()->dataSize(); //unused 00287 00288 //debugLog<<"ProcessDataSegment : Page data size = "<<locPageDataSize<<endl; 00289 unsigned char* locBuff = NULL; 00290 //unsigned long locPacketOffset = 0; 00291 00292 //TODO::: Should this be const ? 00293 //THis is a raw pointer into the segment table, don't delete it. 00294 unsigned char* locSegTable = pendingPage->header()->SegmentTable(); //View only don't delete. 00295 unsigned int locNumSegs = pendingPage->header()->NumPageSegments(); 00296 00297 //debugLog<<"ProcessDataSegment : Num segs = "<<locNumSegs<<endl; 00298 00299 00300 unsigned long locCurrPackSize = 0; 00301 bool locIsFirstPacket = true; 00302 LOOG_INT64 locPrevGranPos = 0; 00303 00304 for (unsigned long i = 0; i < locNumSegs; i++) { 00305 //Packet sums the lacing values of the segment table. 00306 locCurrPackSize += locSegTable[i]; 00307 00308 //If its the last segment in the page or if the lacing value is not 255(ie packet boundary. 00309 00310 /* TRUTH TABLE: 00311 last lacing value lacing value is *not* 255 00312 ================= ========================= 00313 true true } If its the last one or a packet boundary(255 lacing value) we add it. 00314 true false } 00315 false true } 00316 false false If it is a 255 (packet continues) and it's not the last one do nothibng 00317 it is the last lacing value on the page 00318 00319 00320 Lacing values for a Packet never end with 255... if multiple of 255 have a next 0 lacing value. 00321 */ 00322 00323 if ( (locSegTable[i] != 255) || (locNumSegs - 1 == i) ) { 00324 //If its the last lacing value or the the lacing value is not 255 (ie packet boundry) 00325 00326 //This pointer is given to the packet... it deletes it. 00327 locBuff = new unsigned char[locCurrPackSize]; //Given away to constructor of StampedOggPacket. 00328 00329 //STREAM ACCESS::: 00330 //Read data from the stream into the local buffer. 00331 00332 unsigned long locNumRead = mBuffer->read(locBuff, locCurrPackSize); 00333 00334 if (locNumRead < locCurrPackSize) { 00335 //TODO::: Handle this case. 00336 00337 //debugLog<<"ProcessDataSegment : ###### Short read"<<endl; 00338 //debugLog<<"ProcessDataSegment : ** "<<mBuffer->numBytesAvail()<<" avail, "<<mBuffer->spaceLeft()<<" space left."<<endl; 00339 locNumRead = locNumRead; 00340 } 00341 00342 //debugLog<<"Adding packet - size = "<<locCurrPackSize<<endl; 00343 00344 //A packet ends when a lacing value is not 255. So the check for != 255 means the isComplete property of the packet is not set unless the 00345 // lacing value is not equal to 255. 00346 //ERROR CHECK::: 00347 bool locIsContinuation = false; 00348 00349 if (locIsFirstPacket) { 00350 locIsFirstPacket = false; 00351 00352 //Remember what the granule pos was and get it from the new page. 00353 locPrevGranPos = mPrevGranPos; 00354 mPrevGranPos = pendingPage->header()->GranulePos(); 00355 00356 //First packet, check if the continuation flag is set. 00357 if ((pendingPage->header()->HeaderFlags() & OggPageHeader::CONTINUATION) == OggPageHeader::CONTINUATION) { 00358 //Continuation flag is set. 00359 locIsContinuation = true; 00360 } 00361 } 00362 //locBuff is given to the constructor of Stamped Ogg Packet... it deletes it. 00363 //The new StampedPacket is given to the page... it deletes it 00364 pendingPage->addPacket( new StampedOggPacket(locBuff, locCurrPackSize, (locSegTable[i] == 255), locIsContinuation, locPrevGranPos, pendingPage->header()->GranulePos(), StampedOggPacket::OGG_BOTH ) ); 00365 locBuff = NULL; //We've given this away. 00366 //Reset the packet size counter. 00367 locCurrPackSize = 0; 00368 } 00369 } 00370 00371 00372 //Update the state for how many bytes are now needed 00373 mNumBytesNeeded = OggPageHeader::OGG_BASE_HEADER_SIZE; 00374 00375 //debugLog<<"ProcessDataSegment : num bytes needed = "<<mNumBytesNeeded<<endl; 00376 00377 //Dispatch the finished pagbve 00378 eDispatchResult locRet = dispatch(pendingPage); //The dispatch function takes responsibility for this page. 00379 pendingPage = NULL; //We give away the pointer 00380 00381 if (locRet == DISPATCH_OK) { 00382 //debugLog<<"ProcessDataSegment : Transition to AWAITING_BASE_HEADER"<<endl; 00383 mState = AWAITING_BASE_HEADER; 00384 return PROCESS_OK; 00385 } else if (locRet == DISPATCH_FALSE) { 00386 mState = AWAITING_BASE_HEADER; 00387 return PROCESS_DISPATCH_FALSE; 00388 } else { 00389 //debugLog<<"ProcessDataSegment : Dispatch failed."<<endl; 00390 return PROCESS_DISPATCH_FAILED; 00391 } 00392 00393 } 00394 void OggDataBuffer::clearData() { 00395 mBuffer->reset(); 00396 mPrevGranPos = 0; 00397 //debugLog<<"ClearData : Transition back to AWAITING_BASE_HEADER"<<endl; 00398 00399 00400 mNumBytesNeeded = OggPageHeader::OGG_BASE_HEADER_SIZE; 00401 mState = AWAITING_BASE_HEADER; 00402 00404 } 00405 00406 OggDataBuffer::eProcessResult OggDataBuffer::processBuffer() { 00407 00408 eProcessResult locResult = PROCESS_OK; 00409 00410 while (numBytesAvail() >= mNumBytesNeeded) { 00412 switch (mState) { 00413 00414 //QUERY::: Should it be a bug when the if state inside the switch falls through,... potential for infinite loop. 00415 case AWAITING_BASE_HEADER: 00416 //debugLog<<"ProcessBuffer : State = AWAITING_BASE_HEADER"<<endl; 00417 00418 //If theres enough data to form the base header 00419 if (numBytesAvail() >= OggPageHeader::OGG_BASE_HEADER_SIZE) { 00420 //debugLog<<"ProcessBuffer : Enough to process..."<<endl; 00421 00422 locResult = processBaseHeader(); 00423 00424 if (locResult != PROCESS_OK) { 00425 mState = LOST_PAGE_SYNC; 00426 //Base header process failed 00427 return locResult; 00428 } 00429 } 00430 break; 00431 00432 case AWAITING_SEG_TABLE: 00433 //debugLog<<"ProcessBuffer : State = AWAITING_SEG_TABLE"<<endl; 00434 00435 //If there is enough data to get the segt table 00436 if (numBytesAvail() >= pendingPage->header()->NumPageSegments()) { 00437 //debugLog<<"ProcessBuffer : Enough to process..."<<endl; 00438 00439 locResult = processSegTable(); 00440 00441 if (locResult != PROCESS_OK) { 00442 mState = LOST_PAGE_SYNC; 00443 //segment table process failed 00444 return locResult; 00445 } 00446 } 00447 break; 00448 00449 case AWAITING_DATA: 00450 //debugLog<<"ProcessBuffer : State = AWAITING_DATA"<<endl; 00451 //If all the data segment is available 00452 if (numBytesAvail() >= pendingPage->header()->dataSize()) { 00453 //debugLog<<"ProcessBuffer : Enough to process..."<<endl; 00454 00455 //FIX::: Need error check. 00456 locResult = processDataSegment(); 00457 00458 if (locResult == PROCESS_DISPATCH_FAILED) { 00459 mState = LOST_PAGE_SYNC; 00460 //segment table process failed 00461 return locResult; 00462 } 00463 00464 } 00465 break; 00466 case LOST_PAGE_SYNC: 00467 //TODO::: Insert resync code here. 00468 00469 //debugLog<<"ProcessBuffer : State = LOST_PAGE_SYNC"<<endl; 00470 return PROCESS_LOST_SYNC; 00471 default: 00472 //TODO::: What are we supposed to do with this. Anything need cleaning up ? 00473 00474 //debugLog<<"ProcessBuffer : Ogg Buffer Error"<<endl; 00475 return PROCESS_UNKNOWN_INTERNAL_ERROR; 00476 break; 00477 } 00478 } 00479 00480 //There wasn't enough data to progress if we are here. 00481 return locResult; 00482 00483 } 00484 00485 //Debug Only 00486 void OggDataBuffer::debugWrite(string inString) { 00487 //debugLog<<inString<<endl; 00488 }