Torque3D Documentation / _generateds / oggInputStream.cpp

oggInputStream.cpp

Engine/source/core/ogg/oggInputStream.cpp

More...

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