oggInputStream.cpp
Engine/source/core/ogg/oggInputStream.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "core/ogg/oggInputStream.h" 25#include "core/stream/stream.h" 26#include "core/util/safeDelete.h" 27 28 29//#define DEBUG_SPEW 30 31 32//----------------------------------------------------------------------------- 33// OggDecoder implementation. 34//----------------------------------------------------------------------------- 35 36OggDecoder::OggDecoder( const ThreadSafeRef< OggInputStream>& stream ) 37 : mOggStream( stream ) 38{ 39} 40 41OggDecoder::~OggDecoder() 42{ 43 ogg_stream_clear( &mOggStreamState ); 44} 45 46void OggDecoder::_setStartPage( ogg_page* startPage ) 47{ 48 ogg_stream_init( &mOggStreamState, ogg_page_serialno( startPage ) ); 49 ogg_stream_pagein( &mOggStreamState, startPage ); 50} 51 52bool OggDecoder::_readNextPacket( ogg_packet* packet ) 53{ 54 MutexHandle mutex; 55 mutex.lock( &mMutex, true ); 56 57 while( 1 ) 58 { 59 S32 result = ogg_stream_packetout( &mOggStreamState, packet ); 60 if( result == 0 ) 61 { 62 if( !mOggStream->_requestData() ) 63 return false; 64 } 65 else if( result < 0 ) 66 return false; 67 else 68 { 69 #ifdef DEBUG_SPEW 70 Platform::outputDebugString( "[OggDecoder] read packet %i in %s (bytes: %i, bos: %s, eos: %s)", 71 ( U32 ) packet->packetno, 72 getName(), 73 ( U32 ) packet->bytes, 74 packet->b_o_s ? "1" : "0", 75 packet->e_o_s ? "1" : "0" ); 76 #endif 77 78 return true; 79 } 80 } 81} 82 83bool OggDecoder::_nextPacket() 84{ 85 MutexHandle mutex; 86 mutex.lock( &mMutex, true ); 87 88 ogg_packet packet; 89 90 do 91 { 92 if( !_readNextPacket( &packet ) ) 93 return false; 94 } 95 while( !_packetin( &packet ) ); 96 97 return true; 98} 99 100//----------------------------------------------------------------------------- 101// OggInputStream implementation. 102//----------------------------------------------------------------------------- 103 104OggInputStream::OggInputStream( Stream* stream ) 105 : mIsAtEnd( false ), 106 mStream( stream ) 107{ 108 ogg_sync_init( &mOggSyncState ); 109 110 VECTOR_SET_ASSOCIATION( mConstructors ); 111 VECTOR_SET_ASSOCIATION( mDecoders ); 112} 113 114OggInputStream::~OggInputStream() 115{ 116 _freeDecoders(); 117 ogg_sync_clear( &mOggSyncState ); 118 119 if( mStream ) 120 SAFE_DELETE( mStream ); 121} 122 123OggDecoder* OggInputStream::getDecoder( const String& name ) const 124{ 125 for( U32 i = 0; i < mDecoders.size(); ++ i ) 126 if( name.equal( mDecoders[ i ]->getName(), String::NoCase ) ) 127 return mDecoders[ i ]; 128 129 return NULL; 130} 131 132bool OggInputStream::isAtEnd() 133{ 134 MutexHandle mutex; 135 mutex.lock( &mMutex, true ); 136 137 return mIsAtEnd; 138} 139 140bool OggInputStream::init() 141{ 142 if( !mStream->hasCapability( Stream::StreamPosition ) ) 143 return false; 144 145 mStream->setPosition( 0 ); 146 147 // Read all beginning-of-stream pages and construct decoders 148 // for all streams we recognize. 149 150 while( 1 ) 151 { 152 // Read next page. 153 154 ogg_page startPage; 155 _pullNextPage( &startPage ); 156 157 // If not a beginning-of-stream page, push it to the decoders 158 // and stop reading headers. 159 160 if( !ogg_page_bos( &startPage ) ) 161 { 162 _pushNextPage( &startPage ); 163 break; 164 } 165 166 // Try the list of constructors for one that consumes 167 // the page. 168 169 for( U32 i = 0; i < mConstructors.size(); ++ i ) 170 { 171 OggDecoder* decoder = mConstructors[ i ]( this ); 172 if( decoder->_detect( &startPage ) ) 173 mDecoders.push_back( decoder ); 174 else 175 delete decoder; 176 } 177 } 178 179 // Initialize decoders and let all them finish up header processing. 180 181 for( U32 i = 0; i < mDecoders.size(); ++ i ) 182 if( !mDecoders[ i ]->_init() ) 183 { 184 delete mDecoders[ i ]; 185 mDecoders.erase( i ); 186 -- i; 187 } 188 189 if( !mDecoders.size() ) 190 return false; 191 192 return true; 193} 194 195void OggInputStream::_freeDecoders() 196{ 197 for( U32 i = 0; i < mDecoders.size(); ++ i ) 198 delete mDecoders[ i ]; 199 mDecoders.clear(); 200} 201 202bool OggInputStream::_pullNextPage( ogg_page* page) 203{ 204 // Read another page. 205 206 while( ogg_sync_pageout( &mOggSyncState, page ) != 1 ) 207 { 208 enum { BUFFER_SIZE = 4096 }; 209 210 // Read more data. 211 212 char* buffer = ogg_sync_buffer( &mOggSyncState, BUFFER_SIZE ); 213 const U32 oldPos = mStream->getPosition(); 214 mStream->read( BUFFER_SIZE, buffer ); 215 216 const U32 numBytes = mStream->getPosition() - oldPos; 217 if( numBytes ) 218 ogg_sync_wrote( &mOggSyncState, numBytes ); 219 else 220 return false; 221 } 222 223 #ifdef DEBUG_SPEW 224 Platform::outputDebugString( "[OggInputStream] pulled next page (header: %i, body: %i)", 225 page->header_len, page->body_len ); 226 #endif 227 228 return true; 229} 230 231void OggInputStream::_pushNextPage( ogg_page* page ) 232{ 233 for( U32 i = 0; i < mDecoders.size(); ++ i ) 234 { 235 MutexHandle mutex; 236 mutex.lock( &mDecoders[ i ]->mMutex, true ); 237 238 ogg_stream_pagein( &mDecoders[ i ]->mOggStreamState, page ); 239 } 240} 241 242bool OggInputStream::_requestData() 243{ 244 // Lock at this level to ensure correct ordering of page writes. 245 // Technically, the proper place to lock would be _pullNextPage 246 // but then it could happen that one thread pushes a page before 247 // another thread gets to push a page that has been read earlier. 248 249 MutexHandle mutex; 250 mutex.lock( &mMutex, true ); 251 252 ogg_page nextPage; 253 254 if( !_pullNextPage( &nextPage ) ) 255 { 256 mIsAtEnd = true; 257 return false; 258 } 259 260 _pushNextPage( &nextPage ); 261 return true; 262} 263