oggInputStream.cpp 6.7 KB

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