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 #include "stdafx.h" 00032 #include <libOOOgg/OggPacketiser.h> 00033 00034 OggPacketiser::OggPacketiser(void) 00035 : mPacketSink(NULL) 00036 , mPendingPacket(NULL) 00037 , mPacketiserState(PKRSTATE_OK) 00038 , mLooseMode(true) //FIX::: This affects the validator. 00039 , mNumIgnorePackets(0) 00040 , mPrevGranPos(0) 00041 , mCurrentGranPos(0) 00042 { 00043 //debugLog.open("g:\\logs\\packetise.log", ios_base::out); 00044 00045 } 00046 OggPacketiser::OggPacketiser(IStampedOggPacketSink* inPacketSink) 00047 : mPacketSink(inPacketSink) 00048 , mPendingPacket(NULL) 00049 , mPacketiserState(PKRSTATE_OK) 00050 , mLooseMode(true) //FIX::: This affects the validator. 00051 , mNumIgnorePackets(0) 00052 , mPrevGranPos(0) 00053 , mCurrentGranPos(0) 00054 { 00055 //debugLog.open("g:\\logs\\packetise.log", ios_base::out); 00056 } 00057 00058 OggPacketiser::~OggPacketiser(void) 00059 { 00060 //Don't delete the packet sink 00061 //debugLog.close(); 00062 } 00063 00064 IStampedOggPacketSink* OggPacketiser::packetSink() { 00065 return mPacketSink; 00066 } 00067 void OggPacketiser::setPacketSink(IStampedOggPacketSink* inPacketSink) { 00068 mPacketSink = inPacketSink; 00069 } 00070 bool OggPacketiser::reset() { 00071 //debugLog<<"Reset : "<<endl; 00072 delete mPendingPacket; 00073 mPendingPacket = NULL; 00074 mNumIgnorePackets = 0; 00075 mPacketiserState = PKRSTATE_OK; 00076 mPrevGranPos = 0; 00077 mCurrentGranPos = 0; 00078 return true; 00079 } 00080 bool OggPacketiser::acceptOggPage(OggPage* inOggPage) { //AOP::: Needs closer look 00081 //All callers to acceptOggPage give away their pointer 00082 // to this function. All functions implementing this interface 00083 // are responsible for deleting this page. All callers 00084 // should NULL their pointer immediately after calling 00085 // to avoid reusing them. 00086 // 00087 00088 //debugLog<<"acceptOggPage : Gran = "<<inOggPage->header()->GranulePos()<<"Num packs = "<<inOggPage->numPackets()<<endl; 00089 00090 //If the page isn't a -1 page and it's got a different granpos save it. 00091 if ( (inOggPage->header()->GranulePos() != -1) && (inOggPage->header()->GranulePos() != mCurrentGranPos)) { 00092 mPrevGranPos = mCurrentGranPos; 00093 00094 //If the previous is higher than the 00095 if (mPrevGranPos > mCurrentGranPos) { 00096 mPrevGranPos = -1; 00097 } 00098 mCurrentGranPos = inOggPage->header()->GranulePos(); 00099 } 00100 00101 //If the page header says its a continuation page... 00102 if ((inOggPage->header()->HeaderFlags() & 1) == 1) { 00103 //debugLog<<"acceptOggPage : Page says cont..."<<endl; 00104 00106 if (inOggPage->numPackets() > 0) { 00107 //debugLog<<"acceptOggPage : ...and there is at least 1 packet..."<<endl; 00108 00109 //... and we were expecting a continuation... 00110 if (mPacketiserState == PKRSTATE_AWAITING_CONTINUATION) { 00111 //debugLog<<"acceptOggPage : ... and we were waiting for a cont..."<<endl; 00112 00113 //... and the first packet is marked as a continuation... 00114 if (inOggPage->getStampedPacket(0)->isContinuation()) { 00115 //debugLog<<"acceptOggPage : ... and the first packet is a cont..."<<endl; 00116 00117 //... merge this packet into our pending page. 00118 //ASSERT when mPacketiserState = PKRSTATE_AWAITING_CONTINUATION, mPending page != NULL 00119 mPendingPacket->merge(inOggPage->getStampedPacket(0)); 00120 00121 //If even after merging this packet is still truncated... 00122 if (mPendingPacket->isTruncated()) { 00123 //debugLog<<"acceptOggPage : ... but the pending packet is still truncated..."<<endl; 00124 //Packet still not full. special case full page. 00125 //=== 00126 // The only way the the pending packet can be truncated is if 00127 // the first packet in the page is truncated, and the first 00128 // packet in a page can only be truncated if it's also the 00129 // only packet on the page. 00130 //Considering it is incomplete ending a page the granule pos 00131 // will be -1. 00132 //This is a special type of page : 00133 // 1 incomlpete packet on the page 00134 // Continuation flag set 00135 // No complete packets end on this page 00136 // Granule pos is -1 00137 00138 //debugLog<<"acceptOggPage : Go to cont state."<<endl; 00139 //We are still waiting for another continuation... 00140 mPacketiserState = PKRSTATE_AWAITING_CONTINUATION; //This should be redundant, we should already be in this state. 00141 //First packet on page is now merged into pending packet. 00142 } else { 00143 //debugLog<<"acceptOggPage : ... now we can deliver it..."<<endl; 00144 //... the pending packet is now complete. 00145 00146 //TODO::: Static alternative here ? 00147 00148 //Deliver the packet to the packet sink... 00149 if (dispatchStampedOggPacket(mPendingPacket) == false) { 00150 //debugLog<<"acceptOggPage : DELIVERY FAILED !"<<endl; 00151 delete inOggPage; 00152 return false; 00153 } 00154 //debugLog<<"acceptOggPage : ... delivery sucessful..."<<endl; 00155 //debugLog<<"acceptOggPage : Back to OK State..."<<endl; 00156 //Go back to OK state 00157 mPacketiserState = PKRSTATE_OK; 00158 mPendingPacket = NULL; 00159 //First packet on page is merged and delivered. 00160 } 00161 //debugLog<<"acceptOggPage : Send all the other packets besides first and last..."<<endl; 00162 //Send every packet except the first and last to the packet sink. 00163 processPage(inOggPage, false, false); 00164 } else { 00165 //debugLog<<"acceptOggPage : INTERNAL ERROR - Header says cont but packet doesn't."<<endl; 00166 //Header flag says continuation but first packet is not continued. 00167 mPacketiserState = PKRSTATE_INVALID_STREAM; 00168 delete inOggPage; 00169 throw 0; 00170 } 00171 } else { 00172 //debugLog<<"acceptOggPage : UNEXPECTED CONT !"<<endl; 00173 if (mLooseMode == true) { 00174 //debugLog<<"acceptOggPage : ... but we are ignoring it !"<<endl; 00175 //Just ignore when we get continuation pages, just drop the broken bit of packet. 00176 00177 mPendingPacket = NULL; //MEMCHECK::: Did i just leak memory ? 00178 mPacketiserState = PKRSTATE_OK; 00179 00180 //TODO::: Should really return false here if this returns false. 00181 if( processPage(inOggPage, false, false) == false) { 00182 //TODO::: State change ??? 00183 delete inOggPage; 00184 return false; 00185 } 00186 } else { 00187 //debugLog<<"acceptOggPage : FAILURE !!!!"<<endl; 00188 //Unexpected continuation 00189 mPacketiserState = PKRSTATE_INVALID_STREAM; 00190 throw 0; 00191 } 00192 } 00193 } else { 00194 //debugLog<<"acceptOggPage : UNKNOWN CASE"<<endl; 00195 //Is this something ? 00196 //UNKNOWN CASE::: Header continuation flag set, but no packets on page. 00197 mPacketiserState = PKRSTATE_INVALID_STREAM; 00198 delete inOggPage; 00199 throw 0; 00200 } 00201 } else { 00202 //debugLog<<"acceptOggPage : We have a normal page... dumping all but the last..."<<endl; 00203 //Normal page, no continuations... just dump the packets, except the last one 00204 if (inOggPage->numPackets() == 1) { 00205 00206 //I think the bug is here... by sending a trunc packet and not updating state. 00207 00208 //debugLog<<"acceptOggPage : Only one packet on this normal page..."<<endl; 00209 00210 if (inOggPage->getPacket(0)->isTruncated()) { 00211 //debugLog<<"acceptOggPage : ...and it's truncated... so we save it."<<endl; 00212 //ASSERT : mPending packet is NULL, because this is not a continuation page. 00213 mPendingPacket = (StampedOggPacket*)inOggPage->getStampedPacket(0)->clone(); 00214 //debugLog<<"acceptOggPage : Moving to CONT state."<<endl; 00215 mPacketiserState = PKRSTATE_AWAITING_CONTINUATION; 00216 00217 } else { 00218 //debugLog<<"acceptOggPage : Only one packet on this normal page..."<<endl; 00219 if (processPage(inOggPage, true, true) == false ) { //If there was only one pack process it. 00220 //debugLog<<"acceptOggPage : FAIL STATE DELIVERY"<<endl; 00221 //TODO::: State change 00222 delete inOggPage; 00223 return false; 00224 } 00225 00226 //We should never go into the if below now as the packet is taken care of. 00227 } 00228 } else { 00229 //debugLog<<"acceptOggPage : More than one packet so dumping all but last..."<<endl; 00230 if (processPage(inOggPage, true, false) == false ) { //If there was only one packet, no packets would be written 00231 //debugLog<<"acceptOggPage : FAIL STATE DELIVERY"<<endl; 00232 //TODO::: State change 00233 delete inOggPage; 00234 return false; 00235 } 00236 } 00237 00238 //The first packet is delivered. 00239 } 00240 00241 //debugLog<<"acceptOggPage : First pack should be delivered..."<<endl; 00242 //ASSERT: By this point something has been done with the first packet. 00243 00244 // It was either merged with pending page and possibly delivered 00245 // or it was delivered by process page. 00246 //Code following assumes the first packet is dealt with already. 00247 00248 //Now we deal with the last packet... 00249 //ASSERT : The last packet has only been sent if there was 1 or less packets. 00250 00251 //If there is at least two packet on the page... ie at least one more packet we haven't processed. 00252 if (inOggPage->numPackets() > 1) { 00253 //debugLog<<"acceptOggPage : There is at least one packet on the page we haven't processed"<<endl; 00254 //... and we are in the OK state 00255 if (mPacketiserState == PKRSTATE_OK) { 00256 //debugLog<<"acceptOggPage : ... and we are in the OK state..."<<endl; 00257 //If the last packet is truncated. 00258 if (inOggPage->getPacket(inOggPage->numPackets() - 1)->isTruncated()) { 00259 //debugLog<<"acceptOggPage : ... but the last packet is trunced... so we save it and wait for cont..."<<endl; 00260 //The last packet is truncated. Save it and await continuation. 00261 00262 //debugLog<<"acceptOggPage : Moving to CONT state..."<<endl; 00263 mPacketiserState = PKRSTATE_AWAITING_CONTINUATION; 00264 00265 //ASSERT when mPacketiserState = OK, mPendingPacket = NULL 00266 mPendingPacket = (StampedOggPacket*)(inOggPage->getStampedPacket(inOggPage->numPackets() - 1)->clone()); 00267 //This packet is not delivered, it waits for a continuation. 00268 } else { 00269 //We are in the OK state, with no pending packets, and the last packet is not truncated. 00270 //debugLog<<"acceptOggPage : The last page is not trunc so we send it..."<<endl; 00271 //Deliver to the packet sink. 00272 if ( dispatchStampedOggPacket( (StampedOggPacket*)(inOggPage->getStampedPacket(inOggPage->numPackets() - 1)->clone()) ) == false ) { 00273 //debugLog<<"acceptOggPage : Delivery failed..."<<endl; 00274 //TODO::: State change ? 00275 delete inOggPage; 00276 return false; 00277 } 00278 //The last packet is complete. So send it. 00279 } 00280 } else if (mPacketiserState == PKRSTATE_AWAITING_CONTINUATION) { 00281 //FIX::: This case should never occur. 00282 //debugLog<<"acceptOggPage : NEVER BE HERE 1"<<endl; 00283 00284 //Packetiser state is not ok... what to do abo8ut it. 00285 00286 //See special page case above. 00287 //This can only happen when we went through the special case above, and kept 00288 // the state in the continuation state. But by definition it is impossible 00289 // for a subsequent packet on this page to be a continuation packet 00290 // as continuation packets can only be the first packet on the page. 00291 //This is more likely to be due to inconsistency of state code than invalidaity 00292 // of file. 00293 mPacketiserState = PKRSTATE_INVALID_STREAM; 00294 delete inOggPage; 00295 throw 0; 00296 } else { 00297 //debugLog<<"acceptOggPage : NEVER BE HERE 2"<<endl; 00298 //Shouldn't be here 00299 mPacketiserState = PKRSTATE_INVALID_STREAM; 00300 delete inOggPage; 00301 throw 0; 00302 } 00303 } else { 00304 //debugLog<<"acceptOggPage : 1 packet on page only, and we've taken care of it."<<endl; 00305 //Zero packets on page. 00306 } 00307 //debugLog<<"acceptOggPage : All ok... returning..."<<endl<<endl; 00308 delete inOggPage; 00309 return true; 00310 } 00311 00312 bool OggPacketiser::processPage(OggPage* inOggPage, bool inIncludeFirst, bool inIncludeLast) { 00313 //Returns false only if one of the acceptStampedOggPacket calls return false... means we should stop sending stuff and return. 00314 bool locIsOK = true; 00315 //debugLog<<"processPage : "<<endl; 00316 //Adjusts the loop parameters so that only packets excluding those specified are written. 00317 for ( int i = ((inIncludeFirst) ? 0 : 1); 00318 i < ((int)inOggPage->numPackets()) - ((inIncludeLast) ? 0 : 1); 00319 i++) 00320 { 00321 //debugLog<<"processPage : Packet "<< i <<endl; 00322 locIsOK = (locIsOK && dispatchStampedOggPacket((StampedOggPacket*)inOggPage->getStampedPacket(i)->clone())); //Gives away new packet. 00323 if (!locIsOK) { 00324 //debugLog<<"processPage : FAIL STATE"<<endl; 00325 //TODO::: State change ??? 00326 return false; 00327 } 00328 } 00329 //debugLog<<"processPage : returning..."<<endl; 00330 return true; 00331 00332 } 00333 00334 bool OggPacketiser::dispatchStampedOggPacket(StampedOggPacket* inPacket) { //Accepts packet... and gives it away or deletes it. 00335 if (mNumIgnorePackets > 0) { 00336 //Ignore this packet. 00337 mNumIgnorePackets--; 00338 00339 //MEMCHECK::: Should probably delete this packet here.] 00340 delete inPacket; 00341 return true; 00342 } else { 00343 //Modify the header packet to include the gran pos of previous page. 00344 if (mPrevGranPos != -1) { 00345 inPacket->setTimeStamp(mPrevGranPos, mCurrentGranPos, StampedOggPacket::OGG_BOTH); 00346 } 00347 //Dispatch it. 00348 return mPacketSink->acceptStampedOggPacket(inPacket); 00349 } 00350 } 00351 00352 void OggPacketiser::setNumIgnorePackets(unsigned long inNumIgnorePackets) { 00353 mNumIgnorePackets = inNumIgnorePackets; 00354 } 00355 unsigned long OggPacketiser::numIgnorePackets() { 00356 return mNumIgnorePackets; 00357 }