sfxVorbisStream.cpp
Engine/source/sfx/media/sfxVorbisStream.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#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