mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-13 07:34:45 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
261
Engine/source/core/ogg/oggInputStream.cpp
Normal file
261
Engine/source/core/ogg/oggInputStream.cpp
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/ogg/oggInputStream.h"
|
||||
#include "core/stream/stream.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// OggDecoder implementation.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
OggDecoder::OggDecoder( const ThreadSafeRef< OggInputStream >& stream )
|
||||
: mOggStream( stream )
|
||||
{
|
||||
}
|
||||
|
||||
OggDecoder::~OggDecoder()
|
||||
{
|
||||
ogg_stream_clear( &mOggStreamState );
|
||||
}
|
||||
|
||||
void OggDecoder::_setStartPage( ogg_page* startPage )
|
||||
{
|
||||
ogg_stream_init( &mOggStreamState, ogg_page_serialno( startPage ) );
|
||||
ogg_stream_pagein( &mOggStreamState, startPage );
|
||||
}
|
||||
|
||||
bool OggDecoder::_readNextPacket( ogg_packet* packet )
|
||||
{
|
||||
MutexHandle mutex;
|
||||
mutex.lock( &mMutex, true );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
int result = ogg_stream_packetout( &mOggStreamState, packet );
|
||||
if( result == 0 )
|
||||
{
|
||||
if( !mOggStream->_requestData() )
|
||||
return false;
|
||||
}
|
||||
else if( result < 0 )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[OggDecoder] read packet %i in %s (bytes: %i, bos: %s, eos: %s)",
|
||||
( U32 ) packet->packetno,
|
||||
getName(),
|
||||
( U32 ) packet->bytes,
|
||||
packet->b_o_s ? "1" : "0",
|
||||
packet->e_o_s ? "1" : "0" );
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OggDecoder::_nextPacket()
|
||||
{
|
||||
MutexHandle mutex;
|
||||
mutex.lock( &mMutex, true );
|
||||
|
||||
ogg_packet packet;
|
||||
|
||||
do
|
||||
{
|
||||
if( !_readNextPacket( &packet ) )
|
||||
return false;
|
||||
}
|
||||
while( !_packetin( &packet ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// OggInputStream implementation.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
OggInputStream::OggInputStream( Stream* stream )
|
||||
: mStream( stream ),
|
||||
mIsAtEnd( false )
|
||||
{
|
||||
ogg_sync_init( &mOggSyncState );
|
||||
|
||||
VECTOR_SET_ASSOCIATION( mConstructors );
|
||||
VECTOR_SET_ASSOCIATION( mDecoders );
|
||||
}
|
||||
|
||||
OggInputStream::~OggInputStream()
|
||||
{
|
||||
_freeDecoders();
|
||||
ogg_sync_clear( &mOggSyncState );
|
||||
|
||||
if( mStream )
|
||||
SAFE_DELETE( mStream );
|
||||
}
|
||||
|
||||
OggDecoder* OggInputStream::getDecoder( const String& name ) const
|
||||
{
|
||||
for( U32 i = 0; i < mDecoders.size(); ++ i )
|
||||
if( name.equal( mDecoders[ i ]->getName(), String::NoCase ) )
|
||||
return mDecoders[ i ];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool OggInputStream::isAtEnd()
|
||||
{
|
||||
MutexHandle mutex;
|
||||
mutex.lock( &mMutex, true );
|
||||
|
||||
return mIsAtEnd;
|
||||
}
|
||||
|
||||
bool OggInputStream::init()
|
||||
{
|
||||
if( !mStream->hasCapability( Stream::StreamPosition ) )
|
||||
return false;
|
||||
|
||||
mStream->setPosition( 0 );
|
||||
|
||||
// Read all beginning-of-stream pages and construct decoders
|
||||
// for all streams we recognize.
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
// Read next page.
|
||||
|
||||
ogg_page startPage;
|
||||
_pullNextPage( &startPage );
|
||||
|
||||
// If not a beginning-of-stream page, push it to the decoders
|
||||
// and stop reading headers.
|
||||
|
||||
if( !ogg_page_bos( &startPage ) )
|
||||
{
|
||||
_pushNextPage( &startPage );
|
||||
break;
|
||||
}
|
||||
|
||||
// Try the list of constructors for one that consumes
|
||||
// the page.
|
||||
|
||||
for( U32 i = 0; i < mConstructors.size(); ++ i )
|
||||
{
|
||||
OggDecoder* decoder = mConstructors[ i ]( this );
|
||||
if( decoder->_detect( &startPage ) )
|
||||
mDecoders.push_back( decoder );
|
||||
else
|
||||
delete decoder;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize decoders and let all them finish up header processing.
|
||||
|
||||
for( U32 i = 0; i < mDecoders.size(); ++ i )
|
||||
if( !mDecoders[ i ]->_init() )
|
||||
{
|
||||
delete mDecoders[ i ];
|
||||
mDecoders.erase( i );
|
||||
-- i;
|
||||
}
|
||||
|
||||
if( !mDecoders.size() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OggInputStream::_freeDecoders()
|
||||
{
|
||||
for( U32 i = 0; i < mDecoders.size(); ++ i )
|
||||
delete mDecoders[ i ];
|
||||
mDecoders.clear();
|
||||
}
|
||||
|
||||
bool OggInputStream::_pullNextPage( ogg_page* page)
|
||||
{
|
||||
// Read another page.
|
||||
|
||||
while( ogg_sync_pageout( &mOggSyncState, page ) != 1 )
|
||||
{
|
||||
enum { BUFFER_SIZE = 4096 };
|
||||
|
||||
// Read more data.
|
||||
|
||||
char* buffer = ogg_sync_buffer( &mOggSyncState, BUFFER_SIZE );
|
||||
const U32 oldPos = mStream->getPosition();
|
||||
mStream->read( BUFFER_SIZE, buffer );
|
||||
|
||||
const U32 numBytes = mStream->getPosition() - oldPos;
|
||||
if( numBytes )
|
||||
ogg_sync_wrote( &mOggSyncState, numBytes );
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[OggInputStream] pulled next page (header: %i, body: %i)",
|
||||
page->header_len, page->body_len );
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OggInputStream::_pushNextPage( ogg_page* page )
|
||||
{
|
||||
for( U32 i = 0; i < mDecoders.size(); ++ i )
|
||||
{
|
||||
MutexHandle mutex;
|
||||
mutex.lock( &mDecoders[ i ]->mMutex, true );
|
||||
|
||||
ogg_stream_pagein( &mDecoders[ i ]->mOggStreamState, page );
|
||||
}
|
||||
}
|
||||
|
||||
bool OggInputStream::_requestData()
|
||||
{
|
||||
// Lock at this level to ensure correct ordering of page writes.
|
||||
// Technically, the proper place to lock would be _pullNextPage
|
||||
// but then it could happen that one thread pushes a page before
|
||||
// another thread gets to push a page that has been read earlier.
|
||||
|
||||
MutexHandle mutex;
|
||||
mutex.lock( &mMutex, true );
|
||||
|
||||
ogg_page nextPage;
|
||||
|
||||
if( !_pullNextPage( &nextPage ) )
|
||||
{
|
||||
mIsAtEnd = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
_pushNextPage( &nextPage );
|
||||
return true;
|
||||
}
|
||||
173
Engine/source/core/ogg/oggInputStream.h
Normal file
173
Engine/source/core/ogg/oggInputStream.h
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _OGGINPUTSTREAM_H_
|
||||
#define _OGGINPUTSTREAM_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _TYPETRAITS_H_
|
||||
#include "platform/typetraits.h"
|
||||
#endif
|
||||
#ifndef _PLATFORM_THREADS_MUTEX_H_
|
||||
#include "platform/threads/mutex.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEREFCOUNT_H_
|
||||
#include "platform/threads/threadSafeRefCount.h"
|
||||
#endif
|
||||
#include "ogg/ogg.h"
|
||||
|
||||
|
||||
class Stream;
|
||||
class OggInputStream;
|
||||
|
||||
|
||||
/// Single substream in a multiplexed OGG stream.
|
||||
class OggDecoder
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void Parent;
|
||||
friend class OggInputStream;
|
||||
|
||||
protected:
|
||||
|
||||
/// The Ogg container stream.
|
||||
OggInputStream* mOggStream;
|
||||
|
||||
/// The Ogg bitstream.
|
||||
ogg_stream_state mOggStreamState;
|
||||
|
||||
/// Lock for synchronizing access to Ogg stream state.
|
||||
Mutex mMutex;
|
||||
|
||||
/// Read the next packet in the stream.
|
||||
/// @return false if there is no next packet.
|
||||
bool _readNextPacket( ogg_packet* packet );
|
||||
|
||||
///
|
||||
bool _nextPacket();
|
||||
|
||||
///
|
||||
virtual bool _detect( ogg_page* startPage ) = 0;
|
||||
|
||||
///
|
||||
virtual bool _init() = 0;
|
||||
|
||||
///
|
||||
virtual bool _packetin( ogg_packet* packet ) = 0;
|
||||
|
||||
///
|
||||
void _setStartPage( ogg_page* startPage );
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
OggDecoder( const ThreadSafeRef< OggInputStream >& stream );
|
||||
|
||||
virtual ~OggDecoder();
|
||||
|
||||
/// Return the serial number of the Ogg bitstream.
|
||||
U32 getStreamSerialNo() const { return mOggStreamState.serialno; }
|
||||
|
||||
///
|
||||
virtual const char* getName() const = 0;
|
||||
};
|
||||
|
||||
/// A multiplexed OGG input stream feeding into stream decoders.
|
||||
class OggInputStream : public ThreadSafeRefCount< OggInputStream >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void Parent;
|
||||
friend class OggDecoder; // _requestData
|
||||
|
||||
protected:
|
||||
|
||||
typedef OggDecoder* ( *Constructor )( const ThreadSafeRef< OggInputStream >& stream );
|
||||
|
||||
template< typename T >
|
||||
struct _SpellItOutForGCC
|
||||
{
|
||||
static OggDecoder* _fn( const ThreadSafeRef< OggInputStream >& stream )
|
||||
{
|
||||
return constructSingle< T* >( stream );
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
bool mIsAtEnd;
|
||||
|
||||
///
|
||||
Stream* mStream;
|
||||
|
||||
///
|
||||
Vector< Constructor > mConstructors;
|
||||
|
||||
///
|
||||
Vector< OggDecoder* > mDecoders;
|
||||
|
||||
///
|
||||
ogg_sync_state mOggSyncState;
|
||||
|
||||
///
|
||||
Mutex mMutex;
|
||||
|
||||
/// Pull the next page from the OGG stream.
|
||||
bool _pullNextPage( ogg_page* page );
|
||||
|
||||
/// Push the given page to the attached decoder streams.
|
||||
void _pushNextPage( ogg_page* page );
|
||||
|
||||
///
|
||||
bool _requestData();
|
||||
|
||||
///
|
||||
void _freeDecoders();
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
/// @note Ownership of "stream" is transferred to OggInputStream.
|
||||
OggInputStream( Stream* stream );
|
||||
|
||||
~OggInputStream();
|
||||
|
||||
/// Register a decoder class with the stream.
|
||||
template< class T >
|
||||
void addDecoder()
|
||||
{
|
||||
mConstructors.push_back( &_SpellItOutForGCC< T >::_fn );
|
||||
}
|
||||
|
||||
///
|
||||
OggDecoder* getDecoder( const String& name ) const;
|
||||
|
||||
///
|
||||
bool init();
|
||||
|
||||
///
|
||||
bool isAtEnd();
|
||||
};
|
||||
|
||||
#endif // !_OGGINPUTSTREAM_H_
|
||||
694
Engine/source/core/ogg/oggTheoraDecoder.cpp
Normal file
694
Engine/source/core/ogg/oggTheoraDecoder.cpp
Normal file
|
|
@ -0,0 +1,694 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/ogg/oggTheoraDecoder.h"
|
||||
|
||||
#include "gfx/gfxFormatUtils.h"
|
||||
#include "math/mMathFn.h"
|
||||
#include "console/console.h"
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Lookup tables for the transcoders.
|
||||
//
|
||||
// The Y, Cb, and Cr tables are used by both the SSE2 and the generic transcoder.
|
||||
// For the SSE2 code, the data must be 16 byte aligned.
|
||||
//
|
||||
// The clamping table is only used by the generic transcoder. The SSE2 transcoder
|
||||
// uses instructions to implicitly clamp out-of-range values.
|
||||
|
||||
dALIGN( static S32 sRGBY[ 256 ][ 4 ] );
|
||||
dALIGN( static S32 sRGBCb[ 256 ][ 4 ] );
|
||||
dALIGN( static S32 sRGBCr[ 256 ][ 4 ] );
|
||||
|
||||
static U8 sClampBuff[ 1024 ];
|
||||
static U8* sClamp = sClampBuff + 384;
|
||||
|
||||
static void initLookupTables()
|
||||
{
|
||||
static bool sGenerated = false;
|
||||
if( !sGenerated )
|
||||
{
|
||||
for( S32 i = 0; i < 256; ++ i )
|
||||
{
|
||||
// Y.
|
||||
|
||||
sRGBY[ i ][ 0 ] = ( 298 * ( i - 16 ) ) >> 8; // B
|
||||
sRGBY[ i ][ 1 ] = ( 298 * ( i - 16 ) ) >> 8; // G
|
||||
sRGBY[ i ][ 2 ] = ( 298 * ( i - 16 ) ) >> 8; // R
|
||||
sRGBY[ i ][ 3 ] = 0xff; // A
|
||||
|
||||
// Cb.
|
||||
|
||||
sRGBCb[ i ][ 0 ] = ( 516 * ( i - 128 ) + 128 ) >> 8; // B
|
||||
sRGBCb[ i ][ 1 ] = - ( ( 100 * ( i - 128 ) + 128 ) >> 8 ); // G
|
||||
|
||||
// Cr.
|
||||
|
||||
sRGBCr[ i ][ 1 ] = - ( ( 208 * ( i - 128 ) + 128 ) >> 8 ); // B
|
||||
sRGBCr[ i ][ 2 ] = ( 409 * ( i - 128 ) + 128 ) >> 8; // R
|
||||
}
|
||||
|
||||
// Setup clamping table for generic transcoder.
|
||||
|
||||
for( S32 i = -384; i < 640; ++ i )
|
||||
sClamp[ i ] = mClamp( i, 0, 0xFF );
|
||||
|
||||
sGenerated = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline S32 sampleG( U8* pCb, U8* pCr )
|
||||
{
|
||||
return sRGBCr[ *pCr ][ 1 ] + sRGBCr[ *pCb ][ 1 ];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// OggTheoraDecoder.
|
||||
//=============================================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
OggTheoraDecoder::OggTheoraDecoder( const ThreadSafeRef< OggInputStream >& stream )
|
||||
: Parent( stream ),
|
||||
#ifdef TORQUE_DEBUG
|
||||
mLock( 0 ),
|
||||
#endif
|
||||
mTheoraSetup( NULL ),
|
||||
mTheoraDecoder( NULL ),
|
||||
mTranscoder( TRANSCODER_Auto )
|
||||
{
|
||||
// Initialize.
|
||||
|
||||
th_info_init( &mTheoraInfo );
|
||||
th_comment_init( &mTheoraComment );
|
||||
|
||||
initLookupTables();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
OggTheoraDecoder::~OggTheoraDecoder()
|
||||
{
|
||||
// Free packets on the freelist.
|
||||
|
||||
OggTheoraFrame* packet;
|
||||
while( mFreePackets.tryPopFront( packet ) )
|
||||
destructSingle( packet );
|
||||
|
||||
// Clean up libtheora structures.
|
||||
|
||||
if( mTheoraDecoder )
|
||||
th_decode_free( mTheoraDecoder );
|
||||
if( mTheoraSetup )
|
||||
th_setup_free( mTheoraSetup );
|
||||
|
||||
th_comment_clear( &mTheoraComment );
|
||||
th_info_clear( &mTheoraInfo );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool OggTheoraDecoder::_detect( ogg_page* startPage )
|
||||
{
|
||||
_setStartPage( startPage );
|
||||
|
||||
// Read first header packet.
|
||||
|
||||
ogg_packet nextPacket;
|
||||
if( !_readNextPacket( &nextPacket )
|
||||
|| th_decode_headerin( &mTheoraInfo, &mTheoraComment, &mTheoraSetup, &nextPacket ) < 0 )
|
||||
{
|
||||
th_comment_clear( &mTheoraComment );
|
||||
th_info_clear( &mTheoraInfo );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool OggTheoraDecoder::_init()
|
||||
{
|
||||
ogg_packet nextPacket;
|
||||
|
||||
// Read header packets.
|
||||
|
||||
bool haveTheoraHeader = true;
|
||||
while( 1 )
|
||||
{
|
||||
if( !_readNextPacket( &nextPacket ) )
|
||||
{
|
||||
haveTheoraHeader = false;
|
||||
break;
|
||||
}
|
||||
|
||||
int result = th_decode_headerin( &mTheoraInfo, &mTheoraComment, &mTheoraSetup, &nextPacket );
|
||||
if( result < 0 )
|
||||
{
|
||||
haveTheoraHeader = false;
|
||||
break;
|
||||
}
|
||||
else if( result == 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
// Fail if we have no valid and complete Theora header.
|
||||
|
||||
if( !haveTheoraHeader )
|
||||
{
|
||||
th_comment_clear( &mTheoraComment );
|
||||
th_info_clear( &mTheoraInfo );
|
||||
|
||||
Con::errorf( "OggTheoraDecoder::_init() - incorrect or corrupt Theora headers" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init the decoder.
|
||||
|
||||
mTheoraDecoder = th_decode_alloc( &mTheoraInfo, mTheoraSetup );
|
||||
|
||||
// Feed the first video packet to the decoder.
|
||||
|
||||
ogg_int64_t granulePos;
|
||||
th_decode_packetin( mTheoraDecoder, &nextPacket, &granulePos );
|
||||
|
||||
mCurrentFrameTime = th_granule_time( mTheoraDecoder, granulePos );
|
||||
mCurrentFrameNumber = 0;
|
||||
mFrameDuration = 1.f / getFramesPerSecond();
|
||||
|
||||
// Make sure we have a valid pitch.
|
||||
|
||||
if( !mPacketFormat.mPitch )
|
||||
mPacketFormat.mPitch = getFrameWidth() * GFXFormatInfo( mPacketFormat.mFormat ).getBytesPerPixel();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool OggTheoraDecoder::_packetin( ogg_packet* packet )
|
||||
{
|
||||
ogg_int64_t granulePos;
|
||||
|
||||
if( th_decode_packetin( mTheoraDecoder, packet, &granulePos ) != 0 )
|
||||
return false;
|
||||
|
||||
// See if we should drop this frame.
|
||||
//RDTODO: if we have fallen too far behind, start skipping pages
|
||||
|
||||
F32 granuleTime = th_granule_time( mTheoraDecoder, granulePos );
|
||||
mCurrentFrameTime = granuleTime;
|
||||
mCurrentFrameNumber ++;
|
||||
|
||||
bool dropThisFrame = false;
|
||||
TimeSourceRef timeSource = mTimeSource;
|
||||
if( timeSource )
|
||||
{
|
||||
F32 currentTick = F32( timeSource->getPosition() ) / 1000.f;
|
||||
|
||||
if( currentTick >= ( mCurrentFrameTime + mFrameDuration ) )
|
||||
dropThisFrame = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[OggTheoraDecoder] new frame %i at %f sec%s",
|
||||
U32( th_granule_frame( mTheoraDecoder, granulePos ) ),
|
||||
granuleTime,
|
||||
dropThisFrame ? " !! DROPPED !!" : "" );
|
||||
#endif
|
||||
|
||||
return !dropThisFrame;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 OggTheoraDecoder::read( OggTheoraFrame** buffer, U32 num )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
AssertFatal( dCompareAndSwap( mLock, 0, 1 ), "OggTheoraDecoder::read() - simultaneous reads not thread-safe" );
|
||||
#endif
|
||||
|
||||
U32 numRead = 0;
|
||||
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
{
|
||||
// Read and decode a packet.
|
||||
|
||||
if( !_nextPacket() )
|
||||
return numRead; // End of stream.
|
||||
|
||||
// Decode the frame to Y'CbCr.
|
||||
|
||||
th_ycbcr_buffer ycbcr;
|
||||
th_decode_ycbcr_out( mTheoraDecoder, ycbcr );
|
||||
|
||||
// Allocate a packet.
|
||||
|
||||
const U32 width = getFrameWidth();
|
||||
const U32 height = getFrameHeight();
|
||||
|
||||
OggTheoraFrame* packet;
|
||||
if( !mFreePackets.tryPopFront( packet ) )
|
||||
packet = constructSingle< OggTheoraFrame* >( mPacketFormat.mPitch * height );
|
||||
|
||||
packet->mFrameNumber = mCurrentFrameNumber;
|
||||
packet->mFrameTime = mCurrentFrameTime;
|
||||
packet->mFrameDuration = mFrameDuration;
|
||||
|
||||
// Transcode the packet.
|
||||
|
||||
#if ( defined( TORQUE_COMPILER_GCC ) || defined( TORQUE_COMPILER_VISUALC ) ) && defined( TORQUE_CPU_X86 )
|
||||
|
||||
if( ( mTranscoder == TRANSCODER_Auto || mTranscoder == TRANSCODER_SSE2420RGBA ) &&
|
||||
getDecoderPixelFormat() == PIXEL_FORMAT_420 &&
|
||||
Platform::SystemInfo.processor.properties & CPU_PROP_SSE2 &&
|
||||
mPacketFormat.mFormat == GFXFormatR8G8B8A8 &&
|
||||
mTheoraInfo.pic_x == 0 &&
|
||||
mTheoraInfo.pic_y == 0 )
|
||||
{
|
||||
_transcode420toRGBA_SSE2( ycbcr, ( U8* ) packet->data, width, height, mPacketFormat.mPitch );
|
||||
}
|
||||
else
|
||||
|
||||
#endif
|
||||
|
||||
{
|
||||
// Use generic transcoder.
|
||||
|
||||
_transcode( ycbcr, ( U8* ) packet->data, width, height );
|
||||
}
|
||||
|
||||
buffer[ i ] = packet;
|
||||
++ numRead;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
AssertFatal( dCompareAndSwap( mLock, 1, 0 ), "" );
|
||||
#endif
|
||||
|
||||
return numRead;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void OggTheoraDecoder::_transcode( th_ycbcr_buffer ycbcr, U8* buffer, const U32 width, const U32 height )
|
||||
{
|
||||
#define ycbcrToRGB( rgb, pY, pCb, pCr, G ) \
|
||||
{ \
|
||||
GFXPackPixel( \
|
||||
mPacketFormat.mFormat, \
|
||||
rgb, \
|
||||
sClamp[ sRGBY[ *pY ][ 2 ] + sRGBCr[ *pCr ][ 2 ] ], \
|
||||
sClamp[ sRGBY[ *pY ][ 1 ] + G ], \
|
||||
sClamp[ sRGBY[ *pY ][ 0 ] + sRGBCb[ *pCb ][ 0 ] ], \
|
||||
255 \
|
||||
); \
|
||||
}
|
||||
|
||||
// Determine number of chroma samples per 4-pixel luma block.
|
||||
|
||||
U32 numChromaSamples = 4;
|
||||
EPixelFormat pixelFormat = getDecoderPixelFormat();
|
||||
if( pixelFormat == PIXEL_FORMAT_422 )
|
||||
numChromaSamples = 2;
|
||||
else if( pixelFormat == OggTheoraDecoder::PIXEL_FORMAT_420 )
|
||||
numChromaSamples = 1;
|
||||
|
||||
// Convert and copy the pixels. Deal with all three
|
||||
// possible plane configurations.
|
||||
|
||||
const U32 pictOffsetY = _getPictureOffset( ycbcr, 0 );
|
||||
const U32 pictOffsetU = _getPictureOffset( ycbcr, 1 );
|
||||
const U32 pictOffsetV = _getPictureOffset( ycbcr, 2 );
|
||||
|
||||
for( U32 y = 0; y < height; y += 2 )
|
||||
{
|
||||
U8* dst0 = buffer + y * mPacketFormat.mPitch;
|
||||
U8* dst1 = dst0 + mPacketFormat.mPitch;
|
||||
|
||||
U8* pY0 = _getPixelPtr( ycbcr, 0, pictOffsetY, 0, y );
|
||||
U8* pY1 = _getPixelPtr( ycbcr, 0, pictOffsetY, 0, y + 1 );
|
||||
U8* pU0 = _getPixelPtr( ycbcr, 1, pictOffsetU, 0, y );
|
||||
U8* pU1 = _getPixelPtr( ycbcr, 1, pictOffsetU, 0, y + 1 );
|
||||
U8* pV0 = _getPixelPtr( ycbcr, 2, pictOffsetV, 0, y );
|
||||
U8* pV1 = _getPixelPtr( ycbcr, 2, pictOffsetV, 0, y + 1 );
|
||||
|
||||
for( U32 x = 0; x < width; x += 2 )
|
||||
{
|
||||
// Pixel 0x0.
|
||||
|
||||
S32 G = sampleG( pU0, pV0 );
|
||||
|
||||
ycbcrToRGB( dst0, pY0, pU0, pV0, G );
|
||||
|
||||
++ pY0;
|
||||
|
||||
if( numChromaSamples == 4 )
|
||||
{
|
||||
++ pU0;
|
||||
++ pV0;
|
||||
}
|
||||
|
||||
// Pixel 0x1.
|
||||
|
||||
if( numChromaSamples == 4 )
|
||||
G = sampleG( pU0, pV0 );
|
||||
|
||||
ycbcrToRGB( dst0, pY0, pU0, pV0, G );
|
||||
|
||||
++ pY0;
|
||||
++ pU0;
|
||||
++ pV0;
|
||||
|
||||
// Pixel 1x0.
|
||||
|
||||
if( numChromaSamples != 1 )
|
||||
G = sampleG( pU1, pV1 );
|
||||
|
||||
ycbcrToRGB( dst1, pY1, pU1, pV1, G );
|
||||
|
||||
++ pY1;
|
||||
|
||||
if( numChromaSamples == 4 )
|
||||
{
|
||||
++ pU1;
|
||||
++ pV1;
|
||||
}
|
||||
|
||||
// Pixel 1x1.
|
||||
|
||||
if( numChromaSamples == 4 )
|
||||
G = sampleG( pU1, pV1 );
|
||||
|
||||
ycbcrToRGB( dst1, pY1, pU1, pV1, G );
|
||||
|
||||
++ pY1;
|
||||
++ pU1;
|
||||
++ pV1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef ycbcrToRGB
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void OggTheoraDecoder::_transcode420toRGBA_SSE2( th_ycbcr_buffer ycbcr, U8* buffer, U32 width, U32 height, U32 pitch )
|
||||
{
|
||||
AssertFatal( width % 2 == 0, "OggTheoraDecoder::_transcode420toRGBA_SSE2() - width must be multiple of 2" );
|
||||
AssertFatal( height % 2 == 0, "OggTheoraDecoder::_transcode420toRGBA_SSE2() - height must be multiple of 2" );
|
||||
|
||||
unsigned char* ydata = ycbcr[ 0 ].data;
|
||||
unsigned char* udata = ycbcr[ 1 ].data;
|
||||
unsigned char* vdata = ycbcr[ 2 ].data;
|
||||
|
||||
S32* ycoeff = ( S32* ) sRGBY;
|
||||
S32* ucoeff = ( S32* ) sRGBCb;
|
||||
S32* vcoeff = ( S32* ) sRGBCr;
|
||||
|
||||
// At the end of a line loop, we need to jump over the padding resulting from the difference
|
||||
// between pitch and width plus jump a whole scanline as we always operate two scanlines
|
||||
// at a time.
|
||||
const U32 stride = pitch - width * 4 + pitch;
|
||||
|
||||
// Same thing for the Y channel.
|
||||
const U32 ystrideDelta = ycbcr[ 0 ].stride - width + ycbcr[ 0 ].stride;
|
||||
const U32 ypitch = ycbcr[ 0 ].stride;
|
||||
|
||||
// U and V only jump a single scanline so we only need to advance by the padding on the
|
||||
// right. Both planes are half-size.
|
||||
const U32 ustrideDelta = ycbcr[ 1 ].stride - width / 2;
|
||||
const U32 vstrideDelta = ycbcr[ 2 ].stride - width / 2;
|
||||
|
||||
#if defined( TORQUE_COMPILER_VISUALC ) && defined( TORQUE_CPU_X86 )
|
||||
|
||||
__asm
|
||||
{
|
||||
mov ecx,height
|
||||
|
||||
hloop:
|
||||
|
||||
push ecx
|
||||
mov ecx,width
|
||||
|
||||
wloop:
|
||||
|
||||
push ecx
|
||||
xor eax,eax
|
||||
|
||||
// Load and accumulate coefficients for U and V in XMM0.
|
||||
|
||||
mov esi,udata
|
||||
mov ebx,ucoeff
|
||||
mov edx,ydata
|
||||
mov al,[esi]
|
||||
xor ecx,ecx
|
||||
mov edi,vdata
|
||||
shl eax,4
|
||||
movdqa xmm0,[ebx+eax]
|
||||
|
||||
mov ebx,vcoeff
|
||||
mov cl,[edi]
|
||||
mov esi,ycoeff
|
||||
shl ecx,4
|
||||
paddd xmm0,[ebx+ecx]
|
||||
xor eax,eax
|
||||
xor ebx,ebx
|
||||
|
||||
// Load coefficients for Y of the four pixels into XMM1-XMM4.
|
||||
|
||||
mov ecx,ypitch
|
||||
mov al,[edx]
|
||||
mov bl,[edx+1]
|
||||
shl eax,4
|
||||
shl ebx,4
|
||||
movdqa xmm1,[esi+eax]
|
||||
movdqa xmm2,[esi+ebx]
|
||||
xor eax,eax
|
||||
xor ebx,ebx
|
||||
|
||||
mov al,[edx+ecx]
|
||||
mov bl,[edx+ecx+1]
|
||||
shl eax,4
|
||||
shl ebx,4
|
||||
movdqa xmm3,[esi+eax]
|
||||
movdqa xmm4,[esi+ebx]
|
||||
|
||||
mov edi,buffer
|
||||
mov ecx,pitch
|
||||
|
||||
// Add Cb and Cr on top of Y.
|
||||
|
||||
paddd xmm1,xmm0
|
||||
paddd xmm2,xmm0
|
||||
paddd xmm3,xmm0
|
||||
paddd xmm4,xmm0
|
||||
|
||||
// Pack pixels together. We need to pack twice per pixel
|
||||
// to go from 32bits via 16bits to 8bits.
|
||||
//
|
||||
// Right now we're simply packing two garbage pixels for the
|
||||
// second packing operation. An alternative would be to pack the
|
||||
// four pixels into one XMM register and then do a packed shuffle
|
||||
// to split out the lower two pixels before the move.
|
||||
|
||||
packssdw xmm1,xmm2
|
||||
packssdw xmm3,xmm4
|
||||
packuswb xmm1,xmm6
|
||||
packuswb xmm3,xmm7
|
||||
|
||||
// Store pixels.
|
||||
|
||||
movq qword ptr [edi],xmm1
|
||||
movq qword ptr [edi+ecx],xmm3
|
||||
|
||||
// Loop width.
|
||||
|
||||
pop ecx
|
||||
|
||||
add ydata,2
|
||||
inc udata
|
||||
inc vdata
|
||||
add buffer,8
|
||||
|
||||
sub ecx,2
|
||||
jnz wloop
|
||||
|
||||
// Loop height.
|
||||
|
||||
pop ecx
|
||||
|
||||
mov ebx,stride
|
||||
mov eax,ystrideDelta
|
||||
mov edi,ustrideDelta
|
||||
mov esi,vstrideDelta
|
||||
|
||||
add buffer,ebx
|
||||
add ydata,eax
|
||||
add udata,edi
|
||||
add vdata,esi
|
||||
|
||||
sub ecx,2
|
||||
jnz hloop
|
||||
};
|
||||
|
||||
#elif defined( TORQUE_COMPILER_GCC ) && defined( TORQUE_CPU_X86 )
|
||||
|
||||
asm( "pushal\n" // Save all general-purpose registers.
|
||||
|
||||
"movl %0,%%ecx\n" // Load height into ECX.
|
||||
|
||||
".hloop_sse:\n"
|
||||
|
||||
"pushl %%ecx\n" // Save counter.
|
||||
"movl %1,%%ecx\n" // Load width into ECX.
|
||||
|
||||
".wloop_sse:\n"
|
||||
|
||||
"pushl %%ecx\n" // Save counter.
|
||||
"xorl %%eax,%%eax\n" // Zero out eax for later use.
|
||||
|
||||
// Load and accumulate coefficients for U and V in XMM0.
|
||||
|
||||
"movl %3,%%esi\n" // Load U pointer into ESI.
|
||||
"movl %8,%%ebx\n" // Load U coefficient table into EBX.
|
||||
"movl %2,%%edx\n" // Load Y pointer into EDX.
|
||||
"movb (%%esi),%%al\n" // Load U into AL.
|
||||
"xorl %%ecx,%%ecx\n" // Clear ECX.
|
||||
"movl %4,%%edi\n" // Load V pointer into EDI.
|
||||
"shll $4,%%eax\n" // Multiply EAX by 16 to index into table.
|
||||
"movdqa (%%ebx,%%eax),%%xmm0\n" // Load Cb coefficient into XMM0.
|
||||
|
||||
"movl %9,%%ebx\n" // Load V coefficients table into EBX.
|
||||
"movb (%%edi),%%cl\n" // Load V into CL.
|
||||
"movl %7,%%esi\n" // Load Y coefficients table into ESI.
|
||||
"shll $4,%%ecx\n" // Multiply ECX by 16 to index into table.
|
||||
"paddd (%%ebx,%%ecx),%%xmm0\n" // Add Cr coefficient to Cb coefficient.
|
||||
"xorl %%eax,%%eax\n" // Clear EAX.
|
||||
"xorl %%ebx,%%ebx\n" // Clear EBX.
|
||||
|
||||
// Load coefficients for Y of the four pixels into XMM1-XMM4.
|
||||
|
||||
"movl %14,%%ecx\n" // Load Y pitch into ECX (needed later for lower two pixels).
|
||||
"movb (%%edx),%%al\n" // Load upper-left pixel Y into AL.
|
||||
"movb 1(%%edx),%%bl\n" // Load upper-right pixel Y into BL.
|
||||
"shll $4,%%eax\n" // Multiply EAX by 16 to index into table.
|
||||
"shll $4,%%ebx\n" // Multiply EBX by 16 to index into table.
|
||||
"movdqa (%%esi,%%eax),%%xmm1\n" // Load coefficient for upper-left pixel Y into XMM1.
|
||||
"movdqa (%%esi,%%ebx),%%xmm2\n" // Load coefficient for upper-right pixel Y into XMM2.
|
||||
"xorl %%eax,%%eax\n" // Clear EAX.
|
||||
"xorl %%ebx,%%ebx\n" // Clear EBX.
|
||||
|
||||
"movb (%%edx,%%ecx),%%al\n" // Load lower-left pixel Y into AL.
|
||||
"movb 1(%%edx,%%ecx),%%bl\n" // Load lower-right pixel Y into AL.
|
||||
"shll $4,%%eax\n" // Multiply EAX by 16 to index into table.
|
||||
"shll $4,%%ebx\n" // Multiply EBX by 16 to index into table.
|
||||
"movdqa (%%esi,%%eax),%%xmm3\n" // Load coefficient for lower-left pixel Y into XMM3.
|
||||
"movdqa (%%esi,%%ebx),%%xmm4\n" // Load coefficient for lower-right pixel Y into XMM4.
|
||||
|
||||
"movl %5,%%edi\n" // Load buffer pointer into EDI (for later use).
|
||||
"movl %6,%%ecx\n" // Load pitch into ECX (for later use).
|
||||
|
||||
// Add Cb and Cr on top of Y.
|
||||
|
||||
"paddd %%xmm0,%%xmm1\n" // Add chroma channels to upper-left pixel.
|
||||
"paddd %%xmm0,%%xmm2\n" // Add chroma channels to upper-right pixel.
|
||||
"paddd %%xmm0,%%xmm3\n" // Add chroma channels to lower-left pixel.
|
||||
"paddd %%xmm0,%%xmm4\n" // Add chroma channels to lower-right pixel.
|
||||
|
||||
// Pack pixels together. We need to pack twice per pixel
|
||||
// to go from 32bits via 16bits to 8bits.
|
||||
//
|
||||
// Right now we're simply packing two garbage pixels for the
|
||||
// second packing operation. An alternative would be to pack the
|
||||
// four pixels into one XMM register and then do a packed shuffle
|
||||
// to split out the lower two pixels before the move.
|
||||
|
||||
"packssdw %%xmm2,%%xmm1\n" // Pack 32bit channels together into 16bit channels on upper two pixels.
|
||||
"packssdw %%xmm4,%%xmm3\n" // Pack 32bit channels together into 16bit channels on lower two pixels.
|
||||
"packuswb %%xmm6,%%xmm1\n" // Pack 16bit channels together into 8bit channels on upper two pixels (plus two garbage pixels).
|
||||
"packuswb %%xmm7,%%xmm3\n" // Pack 16bit channels together into 8bit channels on lower two pixels (plus two garbage pixels).
|
||||
|
||||
// Store pixels.
|
||||
|
||||
"movq %%xmm1,(%%edi)\n" // Store upper two pixels.
|
||||
"movq %%xmm3,(%%edi,%%ecx)\n" // Store lower two pixels.
|
||||
|
||||
// Loop width.
|
||||
|
||||
"popl %%ecx\n" // Restore width counter.
|
||||
|
||||
"addl $2,%2\n" // Bump Y pointer by two pixels (1 bpp).
|
||||
"incl %3\n" // Bump U pointer by one pixel (1 bpp).
|
||||
"incl %4\n" // Bump V pointer by one pixel (1 bpp).
|
||||
"addl $8,%5\n" // Bump buffer pointer by two pixels (4 bpp).
|
||||
|
||||
"subl $2,%%ecx\n"
|
||||
"jnz .wloop_sse\n"
|
||||
|
||||
// Loop height.
|
||||
|
||||
"popl %%ecx\n" // Restore height counter.
|
||||
|
||||
"movl %10,%%ebx\n" // Load buffer stride into EBX.
|
||||
"movl %11,%%eax\n" // Load Y stride delta into EAX.
|
||||
"movl %12,%%edi\n" // Load U stride delta into EDI.
|
||||
"movl %13,%%esi\n" // Load V stride delta into ESI.
|
||||
|
||||
"addl %%ebx,%5\n" // Bump buffer pointer by stride delta.
|
||||
"addl %%eax,%2\n" // Bump Y pointer by stride delta.
|
||||
"addl %%edi,%3\n" // Bump U pointer by stride delta.
|
||||
"addl %%esi,%4\n" // Bump V pointer by stride delta.
|
||||
|
||||
"subl $2,%%ecx\n"
|
||||
"jnz .hloop_sse\n"
|
||||
|
||||
"popal\n"
|
||||
:
|
||||
: "m" ( height ), // 0
|
||||
"m" ( width ), // 1
|
||||
"m" ( ydata ), // 2
|
||||
"m" ( udata ), // 3
|
||||
"m" ( vdata ), // 4
|
||||
"m" ( buffer ), // 5
|
||||
"m" ( pitch ), // 6
|
||||
"m" ( ycoeff ), // 7
|
||||
"m" ( ucoeff ), // 8
|
||||
"m" ( vcoeff ), // 9
|
||||
"m" ( stride ), // 10
|
||||
"m" ( ystrideDelta ), // 11
|
||||
"m" ( ustrideDelta ), // 12
|
||||
"m" ( vstrideDelta ), // 13
|
||||
"m" ( ypitch ) // 14
|
||||
);
|
||||
|
||||
#endif
|
||||
}
|
||||
267
Engine/source/core/ogg/oggTheoraDecoder.h
Normal file
267
Engine/source/core/ogg/oggTheoraDecoder.h
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _OGGTHEORADECODER_H_
|
||||
#define _OGGTHEORADECODER_H_
|
||||
|
||||
#ifndef _OGGINPUTSTREAM_H_
|
||||
#include "core/ogg/oggInputStream.h"
|
||||
#endif
|
||||
#ifndef _TSTREAM_H_
|
||||
#include "core/stream/tStream.h"
|
||||
#endif
|
||||
#ifndef _RAWDATA_H_
|
||||
#include "core/util/rawData.h"
|
||||
#endif
|
||||
#ifndef _GFXENUMS_H_
|
||||
#include "gfx/gfxEnums.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEDEQUE_H_
|
||||
#include "platform/threads/threadSafeDeque.h"
|
||||
#endif
|
||||
#include "theora/theoradec.h"
|
||||
|
||||
|
||||
/// A single decoded Theora video frame.
|
||||
class OggTheoraFrame : public RawData
|
||||
{
|
||||
public:
|
||||
|
||||
typedef RawData Parent;
|
||||
|
||||
OggTheoraFrame() {}
|
||||
OggTheoraFrame( S8* data, U32 size, bool ownMemory = false )
|
||||
: Parent( data, size, ownMemory ) {}
|
||||
|
||||
/// Serial number of this frame in the stream.
|
||||
U32 mFrameNumber;
|
||||
|
||||
/// Playtime in seconds at which to display this frame.
|
||||
F32 mFrameTime;
|
||||
|
||||
/// Seconds to display this frame.
|
||||
F32 mFrameDuration;
|
||||
};
|
||||
|
||||
|
||||
/// Decodes a Theora substream into frame packets.
|
||||
///
|
||||
/// Frame packets contain raw pixel data in the set pixel format (default is R8G8B8).
|
||||
/// Reading on a thread is safe, but remember to keep a reference to the OggInputStream
|
||||
/// master from the worker thread.
|
||||
class OggTheoraDecoder : public OggDecoder,
|
||||
public IInputStream< OggTheoraFrame* >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef OggDecoder Parent;
|
||||
|
||||
/// Y'CbCr pixel format of the source video stream.
|
||||
/// For informational purposes only. Packet out is determined
|
||||
/// by PacketFormat.
|
||||
enum EPixelFormat
|
||||
{
|
||||
PIXEL_FORMAT_444, // Full Y, full Cb, full Cr.
|
||||
PIXEL_FORMAT_422, // Full Y, half-width Cb, half-width Cr.
|
||||
PIXEL_FORMAT_420, // Full Y, half-widht+height Cb, half-width+height Cr.
|
||||
PIXEL_FORMAT_Unknown
|
||||
};
|
||||
|
||||
/// Descriptor for surface format that this stream should
|
||||
/// decode into. This saves an otherwise potentitally necessary
|
||||
/// swizzling step.
|
||||
///
|
||||
/// @note The output channel ordering will be in device format, i.e.
|
||||
/// least-significant first.
|
||||
struct PacketFormat
|
||||
{
|
||||
/// Pixel format.
|
||||
GFXFormat mFormat;
|
||||
|
||||
/// Bytes per scanline.
|
||||
U32 mPitch;
|
||||
|
||||
/// Default descriptor sets up for RGB.
|
||||
PacketFormat()
|
||||
: mFormat( GFXFormatR8G8B8 ),
|
||||
mPitch( 0 ) {}
|
||||
|
||||
///
|
||||
PacketFormat( GFXFormat format, U32 pitch )
|
||||
: mFormat( format ),
|
||||
mPitch( pitch ) {}
|
||||
};
|
||||
|
||||
///
|
||||
enum ETranscoder
|
||||
{
|
||||
TRANSCODER_Auto, ///< Auto-detect from current formats and processor capabilities.
|
||||
TRANSCODER_Generic, ///< Generic transcoder that handles all source and target formats; 32bit integer + lookup tables.
|
||||
TRANSCODER_SSE2420RGBA, ///< SSE2 transcoder with fixed 4:2:0 to RGBA conversion; 32bit integer + lookup tables.
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
typedef IPositionable< U32 >* TimeSourceRef;
|
||||
|
||||
/// @name libtheora Data
|
||||
/// @{
|
||||
|
||||
///
|
||||
th_comment mTheoraComment;
|
||||
|
||||
///
|
||||
th_info mTheoraInfo;
|
||||
|
||||
///
|
||||
th_setup_info* mTheoraSetup;
|
||||
|
||||
///
|
||||
th_dec_ctx* mTheoraDecoder;
|
||||
|
||||
/// @}
|
||||
|
||||
///
|
||||
PacketFormat mPacketFormat;
|
||||
|
||||
///
|
||||
F32 mFrameDuration;
|
||||
|
||||
///
|
||||
F32 mCurrentFrameTime;
|
||||
|
||||
///
|
||||
U32 mCurrentFrameNumber;
|
||||
|
||||
/// If this is set, the decoder will drop frames that are
|
||||
/// already outdated with respect to the time source.
|
||||
///
|
||||
/// @note Times are in milliseconds and in video time.
|
||||
TimeSourceRef mTimeSource;
|
||||
|
||||
/// Transcoder to use for color space conversion. If the current
|
||||
/// setting is invalid, will fall back to generic.
|
||||
ETranscoder mTranscoder;
|
||||
|
||||
///
|
||||
ThreadSafeDeque< OggTheoraFrame* > mFreePackets;
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
U32 mLock;
|
||||
#endif
|
||||
|
||||
/// Generic transcoder going from any of the Y'CbCr pixel formats to
|
||||
/// any RGB format (that is supported by GFXFormatUtils).
|
||||
void _transcode( th_ycbcr_buffer ycbcr, U8* buffer, U32 width, U32 height );
|
||||
|
||||
/// Transcoder with fixed 4:2:0 to RGBA conversion using SSE2 assembly.
|
||||
void _transcode420toRGBA_SSE2( th_ycbcr_buffer ycbcr, U8* buffer, U32 width, U32 height, U32 pitch );
|
||||
|
||||
// OggDecoder.
|
||||
virtual bool _detect( ogg_page* startPage );
|
||||
virtual bool _init();
|
||||
virtual bool _packetin( ogg_packet* packet );
|
||||
|
||||
///
|
||||
U32 _getPixelOffset( th_ycbcr_buffer buffer, U32 plane, U32 x, U32 y ) const
|
||||
{
|
||||
switch( getDecoderPixelFormat() )
|
||||
{
|
||||
case PIXEL_FORMAT_444: break;
|
||||
case PIXEL_FORMAT_422: if( plane != 0 ) x >>= 1; break;
|
||||
case PIXEL_FORMAT_420: if( plane != 0 ) { x >>= 1; y >>= 1; } break;
|
||||
|
||||
default:
|
||||
AssertFatal( false, "OggTheoraDecoder::_getPixelOffset() - invalid pixel format" );
|
||||
}
|
||||
|
||||
return ( y * buffer[ plane ].stride + x );
|
||||
}
|
||||
|
||||
///
|
||||
U8* _getPixelPtr( th_ycbcr_buffer buffer, U32 plane, U32 offset, U32 x, U32 y ) const
|
||||
{
|
||||
return ( buffer[ plane ].data + offset + _getPixelOffset( buffer, plane, x, y ) );
|
||||
}
|
||||
|
||||
///
|
||||
U32 _getPictureOffset( th_ycbcr_buffer buffer, U32 plane )
|
||||
{
|
||||
return _getPixelOffset( buffer, plane, mTheoraInfo.pic_x, mTheoraInfo.pic_y );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
OggTheoraDecoder( const ThreadSafeRef< OggInputStream >& stream );
|
||||
|
||||
~OggTheoraDecoder();
|
||||
|
||||
/// Return the width of video image frames in pixels.
|
||||
/// @note This returns the actual picture width rather than Theora's internal encoded frame width.
|
||||
U32 getFrameWidth() const { return mTheoraInfo.pic_width; }
|
||||
|
||||
/// Return the height of video image frames in pixels.
|
||||
/// @note This returns the actual picture height rather than Theora's internal encoded frame height.
|
||||
U32 getFrameHeight() const { return mTheoraInfo.pic_height; }
|
||||
|
||||
///
|
||||
F32 getFramesPerSecond() const { return ( F32( mTheoraInfo.fps_numerator ) / F32( mTheoraInfo.fps_denominator ) ); }
|
||||
|
||||
///
|
||||
EPixelFormat getDecoderPixelFormat() const
|
||||
{
|
||||
switch( mTheoraInfo.pixel_fmt )
|
||||
{
|
||||
case TH_PF_444: return PIXEL_FORMAT_444;
|
||||
case TH_PF_422: return PIXEL_FORMAT_422;
|
||||
case TH_PF_420: return PIXEL_FORMAT_420;
|
||||
default: return PIXEL_FORMAT_Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
const PacketFormat& getPacketFormat() const { return mPacketFormat; }
|
||||
|
||||
///
|
||||
void setPacketFormat( const PacketFormat& format ) { mPacketFormat = format; }
|
||||
|
||||
/// Set the reference time source. Frames will be dropped if the decoder
|
||||
/// falls behind the time of this source.
|
||||
///
|
||||
/// @note The time source must have at least the same lifetime as the decoder.
|
||||
void setTimeSource( const TimeSourceRef& timeSource ) { mTimeSource = timeSource; }
|
||||
|
||||
/// Set the Y'CbCr->RGB transcoder to use.
|
||||
void setTranscoder( ETranscoder transcoder ) { mTranscoder = transcoder; }
|
||||
|
||||
///
|
||||
void reusePacket( OggTheoraFrame* packet ) { mFreePackets.pushBack( packet ); }
|
||||
|
||||
// OggDecoder.
|
||||
virtual const char* getName() const { return "Theora"; }
|
||||
|
||||
// IInputStream.
|
||||
virtual U32 read( OggTheoraFrame** buffer, U32 num );
|
||||
};
|
||||
|
||||
#endif // !_OGGTHEORADECODER_H_
|
||||
195
Engine/source/core/ogg/oggVorbisDecoder.cpp
Normal file
195
Engine/source/core/ogg/oggVorbisDecoder.cpp
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/ogg/oggVorbisDecoder.h"
|
||||
#include "console/console.h"
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
OggVorbisDecoder::OggVorbisDecoder( const ThreadSafeRef< OggInputStream >& stream )
|
||||
: Parent( stream )
|
||||
#ifdef TORQUE_DEBUG
|
||||
, mLock( 0 )
|
||||
#endif
|
||||
{
|
||||
// Initialize.
|
||||
|
||||
vorbis_info_init( &mVorbisInfo );
|
||||
vorbis_comment_init( &mVorbisComment );
|
||||
dMemset( &mVorbisBlock, 0, sizeof( mVorbisBlock ) );
|
||||
dMemset( &mVorbisDspState, 0, sizeof( mVorbisDspState ) );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
OggVorbisDecoder::~OggVorbisDecoder()
|
||||
{
|
||||
vorbis_block_clear( &mVorbisBlock );
|
||||
vorbis_dsp_clear( &mVorbisDspState );
|
||||
vorbis_info_clear( &mVorbisInfo );
|
||||
vorbis_comment_clear( &mVorbisComment );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool OggVorbisDecoder::_detect( ogg_page* startPage )
|
||||
{
|
||||
_setStartPage( startPage );
|
||||
|
||||
// Read initial header packet.
|
||||
|
||||
ogg_packet nextPacket;
|
||||
if( !_readNextPacket( &nextPacket )
|
||||
|| vorbis_synthesis_headerin( &mVorbisInfo, &mVorbisComment, &nextPacket ) < 0 )
|
||||
{
|
||||
vorbis_info_clear( &mVorbisInfo );
|
||||
vorbis_comment_clear( &mVorbisComment );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool OggVorbisDecoder::_init()
|
||||
{
|
||||
// Read header packets.
|
||||
|
||||
bool haveVorbisHeader = true;
|
||||
for( U32 i = 0; i < 2; ++ i )
|
||||
{
|
||||
ogg_packet nextPacket;
|
||||
if( !_readNextPacket( &nextPacket ) )
|
||||
{
|
||||
haveVorbisHeader = false;
|
||||
break;
|
||||
}
|
||||
|
||||
int result = vorbis_synthesis_headerin( &mVorbisInfo, &mVorbisComment, &nextPacket );
|
||||
if( result != 0 )
|
||||
{
|
||||
haveVorbisHeader = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fail if we don't have a complete and valid Vorbis header.
|
||||
|
||||
if( !haveVorbisHeader )
|
||||
{
|
||||
vorbis_info_clear( &mVorbisInfo );
|
||||
vorbis_comment_clear( &mVorbisComment );
|
||||
|
||||
Con::errorf( "OggVorbisDecoder::_init() - Incorrect or corrupt Vorbis headers" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init synthesis.
|
||||
|
||||
vorbis_synthesis_init( &mVorbisDspState, &mVorbisInfo );
|
||||
vorbis_block_init( &mVorbisDspState, &mVorbisBlock );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool OggVorbisDecoder::_packetin( ogg_packet* packet )
|
||||
{
|
||||
return ( vorbis_synthesis( &mVorbisBlock, packet ) == 0 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 OggVorbisDecoder::read( RawData** buffer, U32 num )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
AssertFatal( dCompareAndSwap( mLock, 0, 1 ), "OggVorbisDecoder::read() - simultaneous reads not thread-safe" );
|
||||
#endif
|
||||
|
||||
U32 numRead = 0;
|
||||
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
{
|
||||
float** pcmData;
|
||||
U32 numSamples;
|
||||
|
||||
// Read sample data.
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
numSamples = vorbis_synthesis_pcmout( &mVorbisDspState, &pcmData );
|
||||
if( numSamples )
|
||||
break;
|
||||
else
|
||||
{
|
||||
if( !_nextPacket() )
|
||||
return numRead; // End of stream.
|
||||
|
||||
vorbis_synthesis_blockin( &mVorbisDspState, &mVorbisBlock );
|
||||
}
|
||||
}
|
||||
vorbis_synthesis_read( &mVorbisDspState, numSamples );
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[OggVorbisDecoder] read %i samples", numSamples );
|
||||
#endif
|
||||
|
||||
// Allocate a packet.
|
||||
|
||||
const U32 numChannels = getNumChannels();
|
||||
RawData* packet = constructSingle< RawData* >( numSamples * 2 * numChannels ); // Two bytes per channel.
|
||||
|
||||
// Convert and copy the samples.
|
||||
|
||||
S16* samplePtr = ( S16* ) packet->data;
|
||||
for( U32 n = 0; n < numSamples; ++ n )
|
||||
for( U32 c = 0; c < numChannels; ++ c )
|
||||
{
|
||||
S32 val = S32( pcmData[ c ][ n ] * 32767.f );
|
||||
if( val > 32767 )
|
||||
val = 32767;
|
||||
else if( val < -34768 )
|
||||
val = -32768;
|
||||
|
||||
*samplePtr = val;
|
||||
++ samplePtr;
|
||||
}
|
||||
|
||||
// Success.
|
||||
|
||||
buffer[ i ] = packet;
|
||||
numRead ++;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
AssertFatal( dCompareAndSwap( mLock, 1, 0 ), "" );
|
||||
#endif
|
||||
|
||||
return numRead;
|
||||
}
|
||||
91
Engine/source/core/ogg/oggVorbisDecoder.h
Normal file
91
Engine/source/core/ogg/oggVorbisDecoder.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _OGGVORBISDECODER_H_
|
||||
#define _OGGVORBISDECODER_H_
|
||||
|
||||
#ifndef _OGGINPUTSTREAM_H_
|
||||
#include "core/ogg/oggInputStream.h"
|
||||
#endif
|
||||
#ifndef _TSTREAM_H_
|
||||
#include "core/stream/tStream.h"
|
||||
#endif
|
||||
#ifndef _RAWDATA_H_
|
||||
#include "core/util/rawData.h"
|
||||
#endif
|
||||
#include "vorbis/codec.h"
|
||||
|
||||
|
||||
/// Decodes a Vorbis substream into sample packets.
|
||||
///
|
||||
/// Vorbis samples are always 16bits.
|
||||
class OggVorbisDecoder : public OggDecoder,
|
||||
public IInputStream< RawData* >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef OggDecoder Parent;
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
vorbis_info mVorbisInfo;
|
||||
|
||||
///
|
||||
vorbis_comment mVorbisComment;
|
||||
|
||||
///
|
||||
vorbis_dsp_state mVorbisDspState;
|
||||
|
||||
///
|
||||
vorbis_block mVorbisBlock;
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
U32 mLock;
|
||||
#endif
|
||||
|
||||
// OggDecoder.
|
||||
virtual bool _detect( ogg_page* startPage );
|
||||
virtual bool _init();
|
||||
virtual bool _packetin( ogg_packet* packet );
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
OggVorbisDecoder( const ThreadSafeRef< OggInputStream >& stream );
|
||||
|
||||
~OggVorbisDecoder();
|
||||
|
||||
///
|
||||
U32 getNumChannels() const { return mVorbisInfo.channels; }
|
||||
|
||||
///
|
||||
U32 getSamplesPerSecond() const { return mVorbisInfo.rate; }
|
||||
|
||||
// OggDecoder.
|
||||
virtual const char* getName() const { return "Vorbis"; }
|
||||
|
||||
// IInputStream.
|
||||
virtual U32 read( RawData** buffer, U32 num );
|
||||
};
|
||||
|
||||
#endif // !_OGGVORBISDECODER_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue