Torque3D Documentation / _generateds / sfxVorbisStream.cpp

sfxVorbisStream.cpp

Engine/source/sfx/media/sfxVorbisStream.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#ifdef TORQUE_OGGVORBIS
 25
 26
 27#include "sfx/media/sfxVorbisStream.h"
 28#include "core/stream/stream.h"
 29#include "console/console.h"
 30
 31
 32SFXVorbisStream* SFXVorbisStream::create( Stream *stream )
 33{
 34   SFXVorbisStream *sfxStream = new SFXVorbisStream();
 35   if ( sfxStream->open( stream, true ) )
 36      return sfxStream;
 37
 38   delete sfxStream;
 39   return NULL;
 40}
 41
 42SFXVorbisStream::SFXVorbisStream()
 43   : mVF( NULL ),
 44     mBitstream(-1),
 45     mBytesRead( 0 )
 46{
 47}
 48
 49SFXVorbisStream::SFXVorbisStream( const SFXVorbisStream& cloneFrom )
 50   : Parent( cloneFrom )
 51{
 52   mVF = NULL;
 53   mBitstream = -1;
 54   mBytesRead = 0;
 55   if( !mStream->hasCapability( Stream::StreamPosition ) )
 56   {
 57      Con::errorf( "SFXVorbisStream::SFXVorbisStream() - Source stream does not allow seeking" );
 58      return;
 59   }
 60   
 61   mStream->setPosition( 0 );
 62   if( !_readHeader() )
 63   {
 64      Con::errorf( "SFXVorbisStream::SFXVorbisStream() - Opening Vorbis stream failed" );
 65      return;
 66   }
 67
 68   ov_pcm_seek( mVF, ov_pcm_tell( cloneFrom.mVF ) );
 69   
 70   mBitstream = cloneFrom.mBitstream;
 71   mBytesRead = cloneFrom.mBytesRead;
 72}
 73
 74SFXVorbisStream::~SFXVorbisStream()
 75{
 76   // We must call close from our own destructor
 77   // and not the base class... as it causes a
 78   // pure virtual runtime assertion.
 79   close();
 80}
 81
 82size_t SFXVorbisStream::_read_func( void *ptr, size_t size, size_t nmemb, void *datasource )
 83{
 84   Stream *stream = reinterpret_cast<Stream*>( datasource );
 85
 86   // Stream::read() returns true if any data was
 87   // read, so we must track the read bytes ourselves.
 88   U32 startByte = stream->getPosition();
 89   stream->read( size * nmemb, ptr );
 90   U32 endByte = stream->getPosition();
 91
 92   // How many did we actually read?
 93   U32 readBytes = ( endByte - startByte );
 94   U32 readItems = readBytes / size;
 95
 96   return readItems;
 97}
 98
 99S32 SFXVorbisStream::_seek_func( void *datasource, ogg_int64_t offset, S32 whence )
100{
101   Stream *stream = reinterpret_cast<Stream*>( datasource );
102
103   U32 newPos = 0; 
104   if ( whence == SEEK_CUR )
105      newPos = stream->getPosition() + (U32)offset;
106   else if ( whence == SEEK_END )
107      newPos = stream->getStreamSize() - (U32)offset;
108   else
109      newPos = (U32)offset;
110
111   return stream->setPosition( newPos ) ? 0 : -1;
112}
113
114long SFXVorbisStream::_tell_func( void *datasource )
115{
116   Stream *stream = reinterpret_cast<Stream*>( datasource );
117   return stream->getPosition();
118}
119
120bool SFXVorbisStream::_openVorbis()
121{
122   mVF = new OggVorbis_File;
123   dMemset( mVF, 0, sizeof( OggVorbis_File ) );
124
125   const bool canSeek = mStream->hasCapability( Stream::StreamPosition );
126
127   ov_callbacks cb;
128   cb.read_func = _read_func;
129   cb.seek_func = canSeek ? _seek_func : NULL;
130   cb.close_func = NULL;
131   cb.tell_func = canSeek ? _tell_func : NULL;
132
133   // Open it.
134   S32 ovResult = ov_open_callbacks( mStream, mVF, NULL, 0, cb );
135   if( ovResult != 0 )
136      return false;
137
138   return true;
139}
140
141bool SFXVorbisStream::_readHeader()
142{
143   if( !_openVorbis() )
144      return false;
145
146   // Fill in the format info.
147   const vorbis_info *vi = getInfo();
148   mFormat.set( vi->channels, vi->channels * 16, vi->rate );
149
150   // Set the sample count.
151   mSamples = getPcmTotal();
152
153   // Reset the bitstream.
154   mBitstream = 0;
155
156   return true;
157}
158
159void SFXVorbisStream::_close()
160{
161   if ( !mVF )
162      return;
163
164   ov_clear( mVF );
165   delete mVF;
166   mVF = NULL;
167   mBitstream = -1;
168}
169
170const vorbis_info* SFXVorbisStream::getInfo( S32 link )
171{
172   AssertFatal( mVF, "SFXVorbisStream::getInfo() - Stream is closed!" );   
173   return ov_info( mVF, link );
174}
175
176const vorbis_comment* SFXVorbisStream::getComment( S32 link )
177{
178   AssertFatal( mVF, "SFXVorbisStream::getComment() - Stream is closed!" );   
179   return ov_comment( mVF, link );
180}
181
182U64 SFXVorbisStream::getPcmTotal( S32 link )
183{
184   AssertFatal( mVF, "SFXVorbisStream::getInfo() - Stream is closed!" );   
185   return ov_pcm_total( mVF, link );
186}
187
188S32 SFXVorbisStream::read( U8 *buffer, 
189                           U32 length, 
190                           S32 *bitstream )
191{
192   AssertFatal( mVF, "SFXVorbisStream::read() - Stream is closed!" );   
193
194   mBitstream = *bitstream;
195
196   #ifdef TORQUE_BIG_ENDIAN
197      static const S32 isBigEndian = 1;
198   #else
199      static const S32 isBigEndian = 0;
200   #endif
201
202   // Vorbis doesn't seem to like reading 
203   // requests longer than this.
204   const U32 MAXREAD = 4096;
205
206   S64 bytesRead = 0;
207   U32 offset = 0;
208   U32 bytesToRead = 0;
209
210   // Since it only returns the result of one packet 
211   // per call, you generally have to loop to read it all.
212   while( offset < length )
213   {
214      if ( ( length - offset ) < MAXREAD )
215         bytesToRead = length - offset;
216      else
217         bytesToRead = MAXREAD;
218
219      bytesRead = ov_read( mVF, (char*)buffer, bytesToRead, isBigEndian, 2, 1, bitstream );
220      if( bytesRead == 0 ) // EOF
221         return offset;
222      else if( bytesRead < 0 )
223      {
224         // We got an error... return the result.
225         return bytesRead;
226      }
227
228      offset += bytesRead;
229      buffer += bytesRead;
230      mBytesRead += bytesRead;
231   }
232
233   // Return the total data read.
234   return offset;
235}
236
237bool SFXVorbisStream::isEOS() const
238{
239   return ( Parent::isEOS() || ( mStream && ov_pcm_tell( mVF ) == mSamples ) );
240}
241
242void SFXVorbisStream::reset()
243{
244   AssertFatal( mVF, "SFXVorbisStream::reset() - Stream is closed!" );
245
246   // There's a bug in libvorbis 1.2.0 that will cause the
247   // ov_pcm_seek* functions to go into an infinite loop on
248   // some files (apparently if they contain Theora streams).
249   // Avoiding to seek when not necessary seems to do the trick
250   // but if it deadlocks here, that's why.
251
252   if( mBytesRead > 0 )
253   {
254      ov_pcm_seek_page( mVF, 0 );
255      mBitstream = 0;
256      mBytesRead = 0;
257   }
258}
259
260U32 SFXVorbisStream::getPosition() const
261{
262   AssertFatal( mVF, "SFXVorbisStream::getPosition() - Stream is closed!" );
263   return U32( ov_pcm_tell( mVF ) ) * mFormat.getBytesPerSample();
264}
265
266void SFXVorbisStream::setPosition( U32 offset )
267{
268   AssertFatal( mVF, "SFXVorbisStream::setPosition() - Stream is closed!" );
269   ov_pcm_seek( mVF, offset / mFormat.getBytesPerSample() );
270}
271
272U32 SFXVorbisStream::read( U8 *buffer, U32 length )
273{
274   S32 result = read( buffer, length, &mBitstream );
275   if ( result < 0 )
276      return 0;
277
278   return result;
279}
280
281#endif // TORQUE_OGGVORBIS
282