mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-27 16:14:46 +00:00
419 lines
14 KiB
C++
419 lines
14 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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 _THEORATEXTURE_H_
|
|
#define _THEORATEXTURE_H_
|
|
|
|
#ifdef TORQUE_OGGTHEORA
|
|
|
|
#ifndef _PLATFORM_H_
|
|
#include "platform/platform.h"
|
|
#endif
|
|
#ifndef _GFXTEXTUREHANDLE_H_
|
|
#include "gfx/gfxTextureHandle.h"
|
|
#endif
|
|
#ifndef _ASYNCPACKETQUEUE_H_
|
|
#include "platform/async/asyncPacketQueue.h"
|
|
#endif
|
|
#ifndef _ASYNCBUFFEREDSTREAM_H_
|
|
#include "platform/async/asyncBufferedStream.h"
|
|
#endif
|
|
#ifndef _TIMESOURCE_H_
|
|
#include "core/util/timeSource.h"
|
|
#endif
|
|
#ifndef _THREADSAFEREFCOUNT_H_
|
|
#include "platform/threads/threadSafeRefCount.h"
|
|
#endif
|
|
#ifndef _RAWDATA_H_
|
|
#include "core/util/rawData.h"
|
|
#endif
|
|
#ifndef _SIMOBJECT_H_
|
|
#include "console/simObject.h"
|
|
#endif
|
|
#ifndef _SFXSTREAM_H_
|
|
#include "sfx/sfxStream.h"
|
|
#endif
|
|
#ifndef _OGGTHEORADECODER_H_
|
|
#include "core/ogg/oggTheoraDecoder.h"
|
|
#endif
|
|
#ifndef _TYPETRAITS_H_
|
|
#include "platform/typetraits.h"
|
|
#endif
|
|
|
|
|
|
|
|
class SFXDescription;
|
|
class SFXSound;
|
|
class SFXVorbisStream;
|
|
|
|
class OggInputStream;
|
|
class OggVorbisDecoder;
|
|
|
|
class Stream;
|
|
|
|
|
|
/// A single frame in the video frame stream.
|
|
///
|
|
/// Frames are uploaded directly into textures by the asynchronous
|
|
/// streaming system. This offloads as much work as possible to the worker
|
|
/// threads and guarantees the smoothest possible playback.
|
|
///
|
|
/// Frame records are re-used and are managed directly by the video frame
|
|
/// stream. The number of textures concurrently used by a Theora stream
|
|
/// is determined by its stream read-ahead.
|
|
class TheoraTextureFrame
|
|
{
|
|
public:
|
|
|
|
typedef void Parent;
|
|
|
|
/// The texture containing the video frame.
|
|
GFXTexHandle mTexture;
|
|
|
|
/// The locked rectangle, if the texture is currently locked.
|
|
/// Frames will remain in locked state except if currently displayed.
|
|
GFXLockedRect* mLockedRect;
|
|
|
|
///
|
|
U32 mFrameNumber;
|
|
|
|
/// The play time in seconds at which to display this frame.
|
|
F32 mFrameTime;
|
|
|
|
/// The duration in seconds to display this frame.
|
|
F32 mFrameDuration;
|
|
|
|
TheoraTextureFrame()
|
|
: mLockedRect( NULL )
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
inline void destructSingle( TheoraTextureFrame* frame )
|
|
{
|
|
// Do nothing.
|
|
}
|
|
|
|
|
|
/// TheoraTexture decodes Ogg Theora files, and their audio.
|
|
///
|
|
/// TheoraTexture objects can be used similarly to TextureObjects. Just
|
|
/// set the video, call play(), and then refresh every frame to get the
|
|
/// latest video. Audio happens automagically.
|
|
///
|
|
/// @note Uses Theora and ogg libraries which are Copyright (C) Xiph.org Foundation
|
|
class TheoraTexture : private IOutputStream< TheoraTextureFrame* >,
|
|
public IPositionable< U32 >
|
|
{
|
|
public:
|
|
|
|
typedef void Parent;
|
|
|
|
protected:
|
|
|
|
typedef IPositionable< U32 > TimeSourceType;
|
|
typedef GenericTimeSource<> TimerType;
|
|
typedef AsyncPacketQueue< TheoraTextureFrame*, TimeSourceType*, IOutputStream< TheoraTextureFrame* >*, F32 > PlaybackQueueType;
|
|
|
|
class FrameStream;
|
|
class AsyncState;
|
|
friend class GuiTheoraCtrl; // accesses OggTheoraDecoder to set transcoder
|
|
|
|
/// Parameters for tuning streaming behavior.
|
|
enum
|
|
{
|
|
/// Number of textures to load in background.
|
|
FRAME_READ_AHEAD = 6,
|
|
};
|
|
|
|
/// WorkItem that reads a frame from a Theora decoder and uploads it into a TheoraTextureFrame.
|
|
///
|
|
/// Loading directly into textures moves the costly uploads out of the main thread into worker
|
|
/// threads. The downside to this is that since we cannot do GFX work on the worker threads,
|
|
/// we need to expect textures to get to us in locked state.
|
|
class FrameReadItem : public ThreadWorkItem
|
|
{
|
|
public:
|
|
|
|
typedef ThreadWorkItem Parent;
|
|
|
|
protected:
|
|
|
|
/// The asynchronous state we belong to. This reference
|
|
/// ensures that all our streaming state stays live for as long as our
|
|
/// work item is in the pipeline.
|
|
ThreadSafeRef< AsyncState > mAsyncState;
|
|
|
|
///
|
|
FrameStream* mFrameStream;
|
|
|
|
/// The frame texture we are loading.
|
|
TheoraTextureFrame* mFrame;
|
|
|
|
// WorkItem.
|
|
virtual void execute();
|
|
|
|
public:
|
|
|
|
///
|
|
FrameReadItem( AsyncBufferedInputStream< TheoraTextureFrame*, IInputStream< OggTheoraFrame* >* >* stream,
|
|
ThreadPool::Context* context );
|
|
};
|
|
|
|
/// Stream filter that turns a stream of OggTheoraFrames into a buffered background stream of TheoraTextureFrame
|
|
/// records.
|
|
///
|
|
/// This streams allocates a fixed amount 'M' of TheoraTextureFrames. Reading the n-th frame from the stream, will
|
|
/// automatically invalidate the (n-M)-th frame.
|
|
class FrameStream : public AsyncSingleBufferedInputStream< TheoraTextureFrame*, IInputStream< OggTheoraFrame* >*, FrameReadItem >
|
|
{
|
|
public:
|
|
|
|
typedef AsyncSingleBufferedInputStream< TheoraTextureFrame*, IInputStream< OggTheoraFrame* >*, FrameReadItem > Parent;
|
|
|
|
protected:
|
|
|
|
friend class FrameReadItem;
|
|
|
|
enum
|
|
{
|
|
/// Number of TheoraTextureFrame records to allocate.
|
|
///
|
|
/// We need to pre-allocate TheoraTextureFrames as we cannot do GFX operations
|
|
/// on the fly on worker threads. This number corresponds to the length of the
|
|
/// buffering queue plus one record that will be returned to the user as the
|
|
/// current frame record.
|
|
NUM_FRAME_RECORDS = FRAME_READ_AHEAD + 1
|
|
};
|
|
|
|
/// Asynchronous state of the texture object.
|
|
/// This is *NOT* a ThreadSafeRef to not create a cycle.
|
|
AsyncState* mAsyncState;
|
|
|
|
/// Wrap-around index into "mFrames."
|
|
U32 mFrameIndex;
|
|
|
|
/// The pre-allocated TheoraTextureFrames.
|
|
TheoraTextureFrame mFrames[ NUM_FRAME_RECORDS ];
|
|
|
|
public:
|
|
|
|
///
|
|
FrameStream( AsyncState* asyncState, bool looping = false );
|
|
|
|
///
|
|
void acquireTextureLocks();
|
|
|
|
///
|
|
void releaseTextureLocks();
|
|
};
|
|
|
|
/// Encapsulation of compound asynchronous state. Allows releasing
|
|
/// the entire state in one go.
|
|
class AsyncState : public ThreadSafeRefCount< AsyncState >,
|
|
private IPositionable< U32 >
|
|
{
|
|
public:
|
|
|
|
typedef void Parent;
|
|
friend class FrameStream; // mDecoderBufferStream
|
|
|
|
protected:
|
|
|
|
typedef AsyncSingleBufferedInputStream< OggTheoraFrame* > DecoderBufferStream;
|
|
|
|
/// Last synchronization position in the video stream. This is what the
|
|
/// Theora decoder gets passed to see if frames are outdated.
|
|
U32 mCurrentTime;
|
|
|
|
/// The Ogg master stream.
|
|
ThreadSafeRef< OggInputStream > mOggStream;
|
|
|
|
/// The raw video decoding stream.
|
|
OggTheoraDecoder* mTheoraDecoder;
|
|
|
|
/// The raw sound decoding stream; NULL if no Vorbis in video or if
|
|
/// Vorbis is streamed separately.
|
|
OggVorbisDecoder* mVorbisDecoder;
|
|
|
|
/// The background-buffered frame stream.
|
|
ThreadSafeRef< FrameStream > mFrameStream;
|
|
|
|
public:
|
|
|
|
///
|
|
AsyncState( const ThreadSafeRef< OggInputStream >& oggStream, bool looping = false );
|
|
|
|
/// Return the Theora decoder substream.
|
|
OggTheoraDecoder* getTheora() const { return mTheoraDecoder; }
|
|
|
|
/// Return the Vorbis decoder substream.
|
|
/// @note If Vorbis streaming is split out into a separate physical substream,
|
|
/// this method will always return NULL even if Vorbis sound is being used.
|
|
OggVorbisDecoder* getVorbis() const { return mVorbisDecoder; }
|
|
|
|
///
|
|
const ThreadSafeRef< FrameStream >& getFrameStream() const { return mFrameStream; }
|
|
|
|
///
|
|
TheoraTextureFrame* readNextFrame();
|
|
|
|
///
|
|
void start();
|
|
|
|
///
|
|
void stop();
|
|
|
|
///
|
|
bool isAtEnd();
|
|
|
|
///
|
|
void syncTime( U32 ms ) { mCurrentTime = ms; }
|
|
|
|
private:
|
|
|
|
// IPositionable.
|
|
virtual U32 getPosition() const { return mCurrentTime; }
|
|
virtual void setPosition( U32 pos ) {}
|
|
};
|
|
|
|
/// The Theora video file.
|
|
String mFilename;
|
|
|
|
/// The SFXDescription used for sound playback. Synthesized if not provided.
|
|
SimObjectPtr< SFXDescription > mSFXDescription;
|
|
|
|
/// If there's a Vorbis stream, this is the sound source used for playback.
|
|
/// Playback will synchronize to this source then.
|
|
SimObjectPtr< SFXSound > mSFXSource;
|
|
|
|
/// The current frame.
|
|
TheoraTextureFrame* mCurrentFrame;
|
|
|
|
/// The queue that synchronizes the writing of frames to the TheoraTexture.
|
|
PlaybackQueueType* mPlaybackQueue;
|
|
|
|
/// The timer for synchronizing playback when there is no audio stream
|
|
/// to synchronize to.
|
|
TimerType mPlaybackTimer;
|
|
|
|
/// Our threaded state.
|
|
ThreadSafeRef< AsyncState > mAsyncState;
|
|
|
|
///
|
|
bool mIsPaused;
|
|
|
|
///
|
|
U32 mLastFrameNumber;
|
|
|
|
/// Number of frames we have dropped so far.
|
|
U32 mNumDroppedFrames;
|
|
|
|
/// Release all dynamic playback state.
|
|
void _reset();
|
|
|
|
/// Initialize video playback.
|
|
void _initVideo();
|
|
|
|
/// Initialize audio playback.
|
|
void _initAudio( const ThreadSafeRef< SFXStream >& stream = NULL );
|
|
|
|
/// Return the Theora decoder stream or NULL.
|
|
OggTheoraDecoder* _getTheora() const { return ( mAsyncState != NULL ? mAsyncState->getTheora() : NULL ); }
|
|
|
|
/// Return the Vorbis decoder stream or NULL.
|
|
OggVorbisDecoder* _getVorbis() const { return ( mAsyncState != NULL ? mAsyncState->getVorbis() : NULL ); }
|
|
|
|
/// Return the object that is acting as our time source.
|
|
TimeSourceType* _getTimeSource() const;
|
|
|
|
///
|
|
void _onTextureEvent( GFXTexCallbackCode code );
|
|
|
|
// IOutputStream.
|
|
virtual void write( TheoraTextureFrame* const* frames, U32 num );
|
|
|
|
public:
|
|
|
|
///
|
|
TheoraTexture();
|
|
|
|
~TheoraTexture();
|
|
|
|
/// Return the width of a single video frame in pixels.
|
|
U32 getWidth() const;
|
|
|
|
/// Return the height of a single video frame in pixels.
|
|
U32 getHeight() const;
|
|
|
|
/// Return the filename of the Theora video file loaded by the player.
|
|
const String& getFilename() const { return mFilename; }
|
|
|
|
/// Load the given Theora video file. Set up audio using the given SFXDescription (must have
|
|
/// streaming enabled). If no SFXDescription is provided, a default description is used.
|
|
bool setFile( const String& filename, SFXDescription* desc = NULL );
|
|
|
|
/// Start video playback.
|
|
void play();
|
|
|
|
/// Pause video playback.
|
|
void pause();
|
|
|
|
/// Stop video playback.
|
|
void stop();
|
|
|
|
/// Refresh the current frame. This should be called before getTexture() to ensure that
|
|
/// the texture returned by getTexture() contains up-to-date contents.
|
|
void refresh();
|
|
|
|
/// Return true if a video file has been loaded and is ready for playback.
|
|
bool isReady() const { return ( mAsyncState != NULL ); }
|
|
|
|
/// Return true if the video is currently playing.
|
|
bool isPlaying() const;
|
|
|
|
/// Return true if the video is currently paused.
|
|
bool isPaused() const { return mIsPaused; }
|
|
|
|
/// Return the sequence number of the current frame. Starts at 0.
|
|
U32 getFrameNumber() const { return mCurrentFrame->mFrameNumber; }
|
|
|
|
/// Return the playback position of the current frame.
|
|
F32 getFrameTime() const { return mCurrentFrame->mFrameTime; }
|
|
|
|
/// Return the number of frames that have been dropped so far.
|
|
U32 getNumDroppedFrames() const { return mNumDroppedFrames; }
|
|
|
|
/// Return the texture containing the current frame. Call refresh() first
|
|
/// to ensure the texture contents are up-to-date.
|
|
const GFXTexHandle& getTexture() const { return mCurrentFrame->mTexture; }
|
|
GFXTexHandle& getTexture() { return mCurrentFrame->mTexture; }
|
|
|
|
// IPositionable.
|
|
virtual U32 getPosition() const { return _getTimeSource()->getPosition(); }
|
|
virtual void setPosition( U32 pos ) {} // Not (yet?) implemented.
|
|
};
|
|
|
|
#endif // TORQUE_OGGTHEORA
|
|
#endif // !_THEORATEXTURE_H_
|