Torque3D/Engine/source/gfx/video/theoraTexture.h
AzaezelX a58f98167f handle missing virtual destructors
clang translation: destructinplace needs to know what to erase.
2024-06-10 13:20:09 -05:00

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 ), mFrameNumber(0), mFrameTime(0.0f), mFrameDuration(0.0f)
{
}
};
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.
void execute() override;
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 );
virtual ~AsyncState() {};
/// 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.
U32 getPosition() const override { return mCurrentTime; }
void setPosition( U32 pos ) override {}
};
/// 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.
void write( TheoraTextureFrame* const* frames, U32 num ) override;
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.
U32 getPosition() const override { return _getTimeSource()->getPosition(); }
void setPosition( U32 pos ) override {} // Not (yet?) implemented.
};
#endif // TORQUE_OGGTHEORA
#endif // !_THEORATEXTURE_H_