aboutsummaryrefslogtreecommitdiff
path: root/video/video_decoder.h
diff options
context:
space:
mode:
Diffstat (limited to 'video/video_decoder.h')
-rw-r--r--video/video_decoder.h690
1 files changed, 579 insertions, 111 deletions
diff --git a/video/video_decoder.h b/video/video_decoder.h
index 3bb75ade09..5abe1d917c 100644
--- a/video/video_decoder.h
+++ b/video/video_decoder.h
@@ -23,10 +23,17 @@
#ifndef VIDEO_DECODER_H
#define VIDEO_DECODER_H
-#include "common/str.h"
-
+#include "audio/mixer.h"
#include "audio/timestamp.h" // TODO: Move this to common/ ?
+#include "common/array.h"
+#include "common/str.h"
+#include "graphics/pixelformat.h"
+namespace Audio {
+class AudioStream;
+class RewindableAudioStream;
+class SeekableAudioStream;
+}
namespace Common {
class Rational;
@@ -34,7 +41,6 @@ class SeekableReadStream;
}
namespace Graphics {
-struct PixelFormat;
struct Surface;
}
@@ -48,10 +54,14 @@ public:
VideoDecoder();
virtual ~VideoDecoder() {}
+ /////////////////////////////////////////
+ // Opening/Closing a Video
+ /////////////////////////////////////////
+
/**
* Load a video from a file with the given name.
*
- * A default implementation using loadStream is provided.
+ * A default implementation using Common::File and loadStream is provided.
*
* @param filename the filename to load
* @return whether loading the file succeeded
@@ -62,6 +72,10 @@ public:
* Load a video from a generic read stream. The ownership of the
* stream object transfers to this VideoDecoder instance, which is
* hence also responsible for eventually deleting it.
+ *
+ * Implementations of this function are required to call addTrack()
+ * for each track in the video upon success.
+ *
* @param stream the stream to load
* @return whether loading the stream succeeded
*/
@@ -69,60 +83,133 @@ public:
/**
* Close the active video stream and free any associated resources.
+ *
+ * All subclasses that need to close their own resources should still
+ * call the base class' close() function at the start of their function.
*/
- virtual void close() = 0;
+ virtual void close();
/**
* Returns if a video stream is currently loaded or not.
*/
- virtual bool isVideoLoaded() const = 0;
+ bool isVideoLoaded() const;
+ /////////////////////////////////////////
+ // Playback Control
+ /////////////////////////////////////////
/**
- * Returns the width of the video's frames.
- * @return the width of the video's frames
+ * Begin playback of the video.
+ *
+ * @note This has no effect is the video is already playing.
*/
- virtual uint16 getWidth() const = 0;
+ void start();
/**
- * Returns the height of the video's frames.
- * @return the height of the video's frames
+ * Stop playback of the video.
+ *
+ * @note This will close() the video if it is not rewindable.
+ * @note If the video is rewindable, the video will be rewound on the
+ * next start() call unless rewind() or seek() is called before then.
*/
- virtual uint16 getHeight() const = 0;
+ void stop();
/**
- * Get the pixel format of the currently loaded video.
+ * Returns if the video is currently playing or not.
+ *
+ * This is not equivalent to the inverse of endOfVideo(). A video keeps
+ * its playing status even after reaching the end of the video. This will
+ * return true after calling start() and will continue to return true
+ * until stop() (or close()) is called.
*/
- virtual Graphics::PixelFormat getPixelFormat() const = 0;
+ bool isPlaying() const { return _isPlaying; }
/**
- * Get the palette for the video in RGB format (if 8bpp or less).
+ * Returns if a video is rewindable or not. The default implementation
+ * polls each track for rewindability.
*/
- virtual const byte *getPalette() { return 0; }
+ virtual bool isRewindable() const;
/**
- * Returns if the palette is dirty or not.
+ * Rewind a video to its beginning.
+ *
+ * If the video is playing, it will continue to play. The default
+ * implementation will rewind each track.
+ *
+ * @return true on success, false otherwise
+ */
+ virtual bool rewind();
+
+ /**
+ * Returns if a video is seekable or not. The default implementation
+ * polls each track for seekability.
+ */
+ virtual bool isSeekable() const;
+
+ /**
+ * Seek to a given time in the video.
+ *
+ * If the video is playing, it will continue to play. The default
+ * implementation will seek each track and must still be called
+ * from any other implementation.
+ *
+ * @param time The time to seek to
+ * @return true on success, false otherwise
*/
- virtual bool hasDirtyPalette() const { return false; }
+ virtual bool seek(const Audio::Timestamp &time);
/**
- * Set the system palette to the palette returned by getPalette.
- * @see getPalette
+ * Pause or resume the video. This should stop/resume any audio playback
+ * and other stuff. The initial pause time is kept so that any timing
+ * variables can be updated appropriately.
+ *
+ * This is a convenience method which automatically keeps track on how
+ * often the video has been paused, ensuring that after pausing an video
+ * e.g. twice, it has to be unpaused twice before actuallying resuming.
+ *
+ * @param pause true to pause the video, false to resume it
*/
- void setSystemPalette();
+ void pauseVideo(bool pause);
+
+ /**
+ * Return whether the video is currently paused or not.
+ */
+ bool isPaused() const { return _pauseLevel != 0; }
+
+ /**
+ * Set the time for this video to end at. At this time in the video,
+ * all audio will stop and endOfVideo() will return true.
+ */
+ void setEndTime(const Audio::Timestamp &endTime);
+
+ /**
+ * Get the stop time of the video (if not set, zero)
+ */
+ Audio::Timestamp getEndTime() const { return _endTime; }
+
+
+ /////////////////////////////////////////
+ // Playback Status
+ /////////////////////////////////////////
+
+ /**
+ * Returns if the video has reached the end or not.
+ * @return true if the video has finished playing or if none is loaded, false otherwise
+ */
+ bool endOfVideo() const;
/**
* Returns the current frame number of the video.
* @return the last frame decoded by the video
*/
- virtual int32 getCurFrame() const { return _curFrame; }
+ int32 getCurFrame() const;
/**
* Returns the number of frames in the video.
* @return the number of frames in the video
*/
- virtual uint32 getFrameCount() const = 0;
+ uint32 getFrameCount() const;
/**
* Returns the time position (in ms) of the current video.
@@ -138,103 +225,456 @@ public:
* completely accurate (since our mixer does not have precise
* timing).
*/
- virtual uint32 getTime() const;
+ uint32 getTime() const;
+
+
+ /////////////////////////////////////////
+ // Video Info
+ /////////////////////////////////////////
+
+ /**
+ * Returns the width of the video's frames.
+ *
+ * By default, this finds the largest width between all of the loaded
+ * tracks. However, a subclass may override this if it does any kind
+ * of post-processing on it.
+ *
+ * @return the width of the video's frames
+ */
+ virtual uint16 getWidth() const;
+
+ /**
+ * Returns the height of the video's frames.
+ *
+ * By default, this finds the largest height between all of the loaded
+ * tracks. However, a subclass may override this if it does any kind
+ * of post-processing on it.
+ *
+ * @return the height of the video's frames
+ */
+ virtual uint16 getHeight() const;
+
+ /**
+ * Get the pixel format of the currently loaded video.
+ */
+ Graphics::PixelFormat getPixelFormat() const;
+
+ /**
+ * Get the duration of the video.
+ *
+ * If the duration is unknown, this will return 0. If this is not
+ * overriden, it will take the length of the longest track.
+ */
+ virtual Audio::Timestamp getDuration() const;
+
+
+ /////////////////////////////////////////
+ // Frame Decoding
+ /////////////////////////////////////////
+
+ /**
+ * Get the palette for the video in RGB format (if 8bpp or less).
+ *
+ * The palette's format is the same as PaletteManager's palette
+ * (interleaved RGB values).
+ */
+ const byte *getPalette();
+
+ /**
+ * Returns if the palette is dirty or not.
+ */
+ bool hasDirtyPalette() const { return _dirtyPalette; }
/**
* Return the time (in ms) until the next frame should be displayed.
*/
- virtual uint32 getTimeToNextFrame() const = 0;
+ uint32 getTimeToNextFrame() const;
/**
* Check whether a new frame should be decoded, i.e. because enough
* time has elapsed since the last frame was decoded.
* @return whether a new frame should be decoded or not
*/
- virtual bool needsUpdate() const;
+ bool needsUpdate() const;
/**
* Decode the next frame into a surface and return the latter.
+ *
+ * A subclass may override this, but must still call this function. As an
+ * example, a subclass may do this to apply some global video scale to
+ * individual track's frame.
+ *
+ * Note that this will call readNextPacket() internally first before calling
+ * the next video track's decodeNextFrame() function.
+ *
* @return a surface containing the decoded frame, or 0
* @note Ownership of the returned surface stays with the VideoDecoder,
* hence the caller must *not* free it.
* @note this may return 0, in which case the last frame should be kept on screen
*/
- virtual const Graphics::Surface *decodeNextFrame() = 0;
-
- /**
- * Returns if the video has finished playing or not.
- * @return true if the video has finished playing or if none is loaded, false otherwise
- */
- virtual bool endOfVideo() const;
+ virtual const Graphics::Surface *decodeNextFrame();
/**
- * Pause or resume the video. This should stop/resume any audio playback
- * and other stuff. The initial pause time is kept so that any timing
- * variables can be updated appropriately.
+ * Set the default high color format for videos that convert from YUV.
*
- * This is a convenience method which automatically keeps track on how
- * often the video has been paused, ensuring that after pausing an video
- * e.g. twice, it has to be unpaused twice before actuallying resuming.
+ * By default, VideoDecoder will attempt to use the screen format
+ * if it's >8bpp and use a 32bpp format when not.
*
- * @param pause true to pause the video, false to resume it
+ * This must be set before calling loadStream().
*/
- void pauseVideo(bool pause);
+ void setDefaultHighColorFormat(const Graphics::PixelFormat &format) { _defaultHighColorFormat = format; }
- /**
- * Return whether the video is currently paused or not.
- */
- bool isPaused() const { return _pauseLevel != 0; }
+
+ /////////////////////////////////////////
+ // Audio Control
+ /////////////////////////////////////////
/**
* Get the current volume at which the audio in the video is being played
* @return the current volume at which the audio in the video is being played
*/
- virtual byte getVolume() const { return _audioVolume; }
+ byte getVolume() const { return _audioVolume; }
/**
* Set the volume at which the audio in the video should be played.
- * This setting remains until reset() is called (which may be called
- * from loadStream() or close()). The default volume is the maximum.
- *
- * @note This function calls updateVolume() by default.
+ * This setting remains until close() is called (which may be called
+ * from loadStream()). The default volume is the maximum.
*
* @param volume The volume at which to play the audio in the video
*/
- virtual void setVolume(byte volume);
+ void setVolume(byte volume);
/**
* Get the current balance at which the audio in the video is being played
* @return the current balance at which the audio in the video is being played
*/
- virtual int8 getBalance() const { return _audioBalance; }
+ int8 getBalance() const { return _audioBalance; }
/**
* Set the balance at which the audio in the video should be played.
- * This setting remains until reset() is called (which may be called
- * from loadStream() or close()). The default balance is 0.
- *
- * @note This function calls updateBalance() by default.
+ * This setting remains until close() is called (which may be called
+ * from loadStream()). The default balance is 0.
*
* @param balance The balance at which to play the audio in the video
*/
- virtual void setBalance(int8 balance);
+ void setBalance(int8 balance);
+
+ /**
+ * Add an audio track from a stream file.
+ *
+ * This calls SeekableAudioStream::openStreamFile() internally
+ */
+ bool addStreamFileTrack(const Common::String &baseName);
+
+
+ // Future API
+ //void setRate(const Common::Rational &rate);
+ //Common::Rational getRate() const;
protected:
/**
- * Resets _curFrame and _startTime. Should be called from every close() function.
+ * An abstract representation of a track in a movie.
+ */
+ class Track {
+ public:
+ Track();
+ virtual ~Track() {}
+
+ /**
+ * The types of tracks this class can be.
+ */
+ enum TrackType {
+ kTrackTypeNone,
+ kTrackTypeVideo,
+ kTrackTypeAudio
+ };
+
+ /**
+ * Get the type of track.
+ */
+ virtual TrackType getTrackType() const = 0;
+
+ /**
+ * Return if the track has finished.
+ */
+ virtual bool endOfTrack() const = 0;
+
+ /**
+ * Return if the track is rewindable.
+ *
+ * If a video is seekable, it does not need to implement this
+ * for it to also be rewindable.
+ */
+ virtual bool isRewindable() const;
+
+ /**
+ * Rewind the video to the beginning.
+ *
+ * If a video is seekable, it does not need to implement this
+ * for it to also be rewindable.
+ *
+ * @return true on success, false otherwise.
+ */
+ virtual bool rewind();
+
+ /**
+ * Return if the track is seekable.
+ */
+ virtual bool isSeekable() const { return false; }
+
+ /**
+ * Seek to the given time.
+ * @param time The time to seek to, from the beginning of the video.
+ * @return true on success, false otherwise.
+ */
+ virtual bool seek(const Audio::Timestamp &time) { return false; }
+
+ /**
+ * Set the pause status of the track.
+ */
+ void pause(bool shouldPause) {}
+
+ /**
+ * Return if the track is paused.
+ */
+ bool isPaused() const { return _paused; }
+
+ /**
+ * Get the duration of the track (starting from this track's start time).
+ *
+ * By default, this returns 0 for unknown.
+ */
+ virtual Audio::Timestamp getDuration() const;
+
+ protected:
+ /**
+ * Function called by pause() for subclasses to implement.
+ */
+ void pauseIntern(bool pause);
+
+ private:
+ bool _paused;
+ };
+
+ /**
+ * An abstract representation of a video track.
+ */
+ class VideoTrack : public Track {
+ public:
+ VideoTrack() {}
+ virtual ~VideoTrack() {}
+
+ TrackType getTrackType() const { return kTrackTypeVideo; }
+ virtual bool endOfTrack() const;
+
+ /**
+ * Get the width of this track
+ */
+ virtual uint16 getWidth() const = 0;
+
+ /**
+ * Get the height of this track
+ */
+ virtual uint16 getHeight() const = 0;
+
+ /**
+ * Get the pixel format of this track
+ */
+ virtual Graphics::PixelFormat getPixelFormat() const = 0;
+
+ /**
+ * Get the current frame of this track
+ *
+ * @see VideoDecoder::getCurFrame()
+ */
+ virtual int getCurFrame() const = 0;
+
+ /**
+ * Get the frame count of this track
+ *
+ * @note If the frame count is unknown, return 0 (which is also
+ * the default implementation of the function). However, one must
+ * also implement endOfTrack() in that case.
+ */
+ virtual int getFrameCount() const { return 0; }
+
+ /**
+ * Get the start time of the next frame in milliseconds since
+ * the start of the video
+ */
+ virtual uint32 getNextFrameStartTime() const = 0;
+
+ /**
+ * Decode the next frame
+ */
+ virtual const Graphics::Surface *decodeNextFrame() = 0;
+
+ /**
+ * Get the palette currently in use by this track
+ */
+ virtual const byte *getPalette() const { return 0; }
+
+ /**
+ * Does the palette currently in use by this track need to be updated?
+ */
+ virtual bool hasDirtyPalette() const { return false; }
+ };
+
+ /**
+ * A VideoTrack that is played at a constant rate.
+ *
+ * If the frame count is unknown, you must override endOfTrack().
+ */
+ class FixedRateVideoTrack : public VideoTrack {
+ public:
+ FixedRateVideoTrack() {}
+ virtual ~FixedRateVideoTrack() {}
+
+ uint32 getNextFrameStartTime() const;
+ virtual Audio::Timestamp getDuration() const;
+
+ protected:
+ /**
+ * Get the rate at which this track is played.
+ */
+ virtual Common::Rational getFrameRate() const = 0;
+ };
+
+ /**
+ * An abstract representation of an audio track.
*/
- void reset();
+ class AudioTrack : public Track {
+ public:
+ AudioTrack() {}
+ virtual ~AudioTrack() {}
+
+ TrackType getTrackType() const { return kTrackTypeAudio; }
+
+ virtual bool endOfTrack() const;
+
+ /**
+ * Start playing this track
+ */
+ void start();
+
+ /**
+ * Stop playing this track
+ */
+ void stop();
+
+ void start(const Audio::Timestamp &limit);
+
+ /**
+ * Get the volume for this track
+ */
+ byte getVolume() const { return _volume; }
+
+ /**
+ * Set the volume for this track
+ */
+ void setVolume(byte volume);
+
+ /**
+ * Get the balance for this track
+ */
+ int8 getBalance() const { return _balance; }
+
+ /**
+ * Set the balance for this track
+ */
+ void setBalance(int8 balance);
+
+ /**
+ * Get the time the AudioStream behind this track has been
+ * running
+ */
+ uint32 getRunningTime() const;
+
+ /**
+ * Get the sound type to be used when playing this audio track
+ */
+ virtual Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kPlainSoundType; }
+
+ protected:
+ void pauseIntern(bool pause);
+
+ /**
+ * Get the AudioStream that is the representation of this AudioTrack
+ */
+ virtual Audio::AudioStream *getAudioStream() const = 0;
+
+ private:
+ Audio::SoundHandle _handle;
+ byte _volume;
+ int8 _balance;
+ };
/**
- * Actual implementation of pause by subclasses. See pause()
- * for details.
+ * An AudioTrack that implements isRewindable() and rewind() using
+ * RewindableAudioStream.
*/
- virtual void pauseVideoIntern(bool pause) {}
+ class RewindableAudioTrack : public AudioTrack {
+ public:
+ RewindableAudioTrack() {}
+ virtual ~RewindableAudioTrack() {}
+
+ bool isRewindable() const { return true; }
+ bool rewind();
+
+ protected:
+ Audio::AudioStream *getAudioStream() const;
+
+ /**
+ * Get the RewindableAudioStream pointer to be used by this class
+ * for rewind() and getAudioStream()
+ */
+ virtual Audio::RewindableAudioStream *getRewindableAudioStream() const = 0;
+ };
/**
- * Add the time the video has been paused to maintain sync
+ * An AudioTrack that implements isSeekable() and seek() using
+ * SeekableAudioStream.
*/
- virtual void addPauseTime(uint32 ms) { _startTime += ms; }
+ class SeekableAudioTrack : public AudioTrack {
+ public:
+ SeekableAudioTrack() {}
+ virtual ~SeekableAudioTrack() {}
+
+ bool isSeekable() const { return true; }
+ bool seek(const Audio::Timestamp &time);
+
+ Audio::Timestamp getDuration() const;
+
+ protected:
+ Audio::AudioStream *getAudioStream() const;
+
+ /**
+ * Get the SeekableAudioStream pointer to be used by this class
+ * for seek(), getDuration(), and getAudioStream()
+ */
+ virtual Audio::SeekableAudioStream *getSeekableAudioStream() const = 0;
+ };
+
+ /**
+ * A SeekableAudioTrack that constructs its SeekableAudioStream using
+ * SeekableAudioStream::openStreamFile()
+ */
+ class StreamFileAudioTrack : public SeekableAudioTrack {
+ public:
+ StreamFileAudioTrack();
+ ~StreamFileAudioTrack();
+
+ /**
+ * Load the track from a file with the given base name.
+ *
+ * @return true on success, false otherwise
+ */
+ bool loadFromFile(const Common::String &baseName);
+
+ protected:
+ Audio::SeekableAudioStream *_stream;
+ Audio::SeekableAudioStream *getSeekableAudioStream() const { return _stream; }
+ };
/**
* Reset the pause start time (which should be called when seeking)
@@ -242,79 +682,107 @@ protected:
void resetPauseStartTime();
/**
- * Update currently playing audio tracks with the new volume setting
+ * Decode enough data for the next frame and enough audio to last that long.
+ *
+ * This function is used by the decodeNextFrame() function. A subclass
+ * of a Track may decide to just have its decodeNextFrame() function read
+ * and decode the frame.
*/
- virtual void updateVolume() {}
+ virtual void readNextPacket() {}
/**
- * Update currently playing audio tracks with the new balance setting
+ * Define a track to be used by this class.
+ *
+ * The pointer is then owned by this base class.
*/
- virtual void updateBalance() {}
+ void addTrack(Track *track);
- int32 _curFrame;
- int32 _startTime;
+ /**
+ * Whether or not getTime() will sync with a playing audio track.
+ *
+ * A subclass can override this to disable this feature.
+ */
+ virtual bool useAudioSync() const { return true; }
-private:
- uint32 _pauseLevel;
- uint32 _pauseStartTime;
- byte _audioVolume;
- int8 _audioBalance;
-};
+ /**
+ * Get the given track based on its index.
+ *
+ * @return A valid track pointer on success, 0 otherwise
+ */
+ Track *getTrack(uint track);
-/**
- * A VideoDecoder wrapper that implements getTimeToNextFrame() based on getFrameRate().
- */
-class FixedRateVideoDecoder : public virtual VideoDecoder {
-public:
- uint32 getTimeToNextFrame() const;
+ /**
+ * Get the given track based on its index
+ *
+ * @return A valid track pointer on success, 0 otherwise
+ */
+ const Track *getTrack(uint track) const;
-protected:
/**
- * Return the frame rate in frames per second.
- * This returns a Rational because videos can have rates that are not integers and
- * there are some videos with frame rates < 1.
+ * Find out if all video tracks have finished
+ *
+ * This is useful if one wants to figure out if they need to buffer all
+ * remaining audio in a file.
*/
- virtual Common::Rational getFrameRate() const = 0;
+ bool endOfVideoTracks() const;
-private:
- uint32 getFrameBeginTime(uint32 frame) const;
-};
+ /**
+ * Get the default high color format
+ */
+ Graphics::PixelFormat getDefaultHighColorFormat() const { return _defaultHighColorFormat; }
-/**
- * A VideoDecoder that can be rewound back to the beginning.
- */
-class RewindableVideoDecoder : public virtual VideoDecoder {
-public:
/**
- * Rewind to the beginning of the video.
+ * Find the video track with the lowest start time for the next frame
*/
- virtual void rewind() = 0;
-};
+ VideoTrack *findNextVideoTrack();
-/**
- * A VideoDecoder that can seek to a frame or point in time.
- */
-class SeekableVideoDecoder : public virtual RewindableVideoDecoder {
-public:
/**
- * Seek to the specified time.
+ * Find the video track with the lowest start time for the next frame
*/
- virtual void seekToTime(const Audio::Timestamp &time) = 0;
+ const VideoTrack *findNextVideoTrack() const;
/**
- * Seek to the specified time (in ms).
+ * Typedef helpers for accessing tracks
*/
- void seekToTime(uint32 msecs) { seekToTime(Audio::Timestamp(msecs, 1000)); }
+ typedef Common::Array<Track *> TrackList;
+ typedef TrackList::iterator TrackListIterator;
/**
- * Implementation of RewindableVideoDecoder::rewind().
+ * Get the begin iterator of the tracks
*/
- virtual void rewind() { seekToTime(0); }
+ TrackListIterator getTrackListBegin() { return _tracks.begin(); }
/**
- * Get the total duration of the video (in ms).
+ * Get the end iterator of the tracks
*/
- virtual uint32 getDuration() const = 0;
+ TrackListIterator getTrackListEnd() { return _tracks.end(); }
+
+private:
+ // Tracks owned by this VideoDecoder
+ TrackList _tracks;
+
+ // Current playback status
+ bool _isPlaying, _needsRewind, _needsUpdate;
+ Audio::Timestamp _lastTimeChange, _endTime;
+ bool _endTimeSet;
+
+ // Palette settings from individual tracks
+ mutable bool _dirtyPalette;
+ const byte *_palette;
+
+ // Default PixelFormat settings
+ Graphics::PixelFormat _defaultHighColorFormat;
+
+ // Internal helper functions
+ void stopAudio();
+ void startAudio();
+ void startAudioLimit(const Audio::Timestamp &limit);
+
+ int32 _startTime;
+ uint32 _pauseLevel;
+ uint32 _pauseStartTime;
+ byte _audioVolume;
+ int8 _audioBalance;
};
} // End of namespace Video