mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 13:45:27 +00:00
b12df215bb
Implement HTMLMediaElement.fastSeek(), basically by changing all the MediaDecoderReader::Seek() overrides to not call MediaDecoderReader::DecodeToTarget(), and have MediaDecoderReader::DecodeSeek() call DecodeToTarget() if we're doing an accurate (non-fast) seek. Update gizmo.mp4 to have a keyframe every second, instead of only 1 keyframe at the start of stream. This makes the unit test I added more useful for mp4... I pushed most of the seek target clamping logic in MediaDecoder up into HTMLMediaElement, so that we're clamping in fewer places. Note MediaDecoderStateMachine::Seek() still sanity checks the seek target. We have to update the currentTime/MediaDecoder playback position after a seek completes now, rather than assuming the seek always got it exactly right. Removed those pesky assertions about seek target lying in the first frame after seek, since actually sometimes the media doesn't have samples for all streams after a seek (either due to the media being encoded like that, or because of a bug in the platform's decoder, not entirely sure). Green: https://tbpl.mozilla.org/?tree=Try&rev=b028258565e2 * * * Bug 778077 - Fix up MediaOMXReader fastseek to ensure audio stream stays in sync with video stream. r=cajbir
180 lines
7.2 KiB
C++
180 lines
7.2 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
#if !defined(MediaDecoderReader_h_)
|
|
#define MediaDecoderReader_h_
|
|
|
|
#include "AbstractMediaDecoder.h"
|
|
#include "MediaInfo.h"
|
|
#include "MediaData.h"
|
|
#include "MediaQueue.h"
|
|
#include "AudioCompactor.h"
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
class TimeRanges;
|
|
}
|
|
|
|
// Encapsulates the decoding and reading of media data. Reading can only be
|
|
// done on the decode thread. Never hold the decoder monitor when
|
|
// calling into this class. Unless otherwise specified, methods and fields of
|
|
// this class can only be accessed on the decode thread.
|
|
class MediaDecoderReader {
|
|
public:
|
|
MediaDecoderReader(AbstractMediaDecoder* aDecoder);
|
|
virtual ~MediaDecoderReader();
|
|
|
|
// Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
|
|
// on failure.
|
|
virtual nsresult Init(MediaDecoderReader* aCloneDonor) = 0;
|
|
|
|
// True if this reader is waiting media resource allocation
|
|
virtual bool IsWaitingMediaResources() { return false; }
|
|
// True when this reader need to become dormant state
|
|
virtual bool IsDormantNeeded() { return false; }
|
|
// Release media resources they should be released in dormant state
|
|
virtual void ReleaseMediaResources() {};
|
|
// Release the decoder during shutdown
|
|
virtual void ReleaseDecoder() {};
|
|
|
|
// Resets all state related to decoding, emptying all buffers etc.
|
|
virtual nsresult ResetDecode();
|
|
|
|
// Decodes an unspecified amount of audio data, enqueuing the audio data
|
|
// in mAudioQueue. Returns true when there's more audio to decode,
|
|
// false if the audio is finished, end of file has been reached,
|
|
// or an un-recoverable read error has occured.
|
|
virtual bool DecodeAudioData() = 0;
|
|
|
|
// Reads and decodes one video frame. Packets with a timestamp less
|
|
// than aTimeThreshold will be decoded (unless they're not keyframes
|
|
// and aKeyframeSkip is true), but will not be added to the queue.
|
|
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
|
int64_t aTimeThreshold) = 0;
|
|
|
|
virtual bool HasAudio() = 0;
|
|
virtual bool HasVideo() = 0;
|
|
|
|
// Read header data for all bitstreams in the file. Fills aInfo with
|
|
// the data required to present the media, and optionally fills *aTags
|
|
// with tag metadata from the file.
|
|
// Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
|
|
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
|
MetadataTags** aTags) = 0;
|
|
|
|
// Stores the presentation time of the first frame we'd be able to play if
|
|
// we started playback at the current position. Returns the first video
|
|
// frame, if we have video.
|
|
virtual VideoData* FindStartTime(int64_t& aOutStartTime);
|
|
|
|
// Moves the decode head to aTime microseconds. aStartTime and aEndTime
|
|
// denote the start and end times of the media in usecs, and aCurrentTime
|
|
// is the current playback position in microseconds.
|
|
virtual nsresult Seek(int64_t aTime,
|
|
int64_t aStartTime,
|
|
int64_t aEndTime,
|
|
int64_t aCurrentTime) = 0;
|
|
|
|
// Called to move the reader into idle/active state. When the reader is
|
|
// created it is assumed to be active (i.e. not idle). When the media
|
|
// element is paused and we don't need to decode any more data, the state
|
|
// machine calls SetIdle() to inform the reader that its decoder won't be
|
|
// needed for a while. When we need to decode data again, the state machine
|
|
// calls SetActive() to activate the decoder. The reader can use these
|
|
// notifications to enter/exit a low power state when the decoder isn't
|
|
// needed, if desired. This is most useful on mobile.
|
|
virtual void SetIdle() { }
|
|
virtual void SetActive() { }
|
|
|
|
// Tell the reader that the data decoded are not for direct playback, so it
|
|
// can accept more files, in particular those which have more channels than
|
|
// available in the audio output.
|
|
void SetIgnoreAudioOutputFormat()
|
|
{
|
|
mIgnoreAudioOutputFormat = true;
|
|
}
|
|
|
|
protected:
|
|
// Queue of audio frames. This queue is threadsafe, and is accessed from
|
|
// the audio, decoder, state machine, and main threads.
|
|
MediaQueue<AudioData> mAudioQueue;
|
|
|
|
// Queue of video frames. This queue is threadsafe, and is accessed from
|
|
// the decoder, state machine, and main threads.
|
|
MediaQueue<VideoData> mVideoQueue;
|
|
|
|
// An adapter to the audio queue which first copies data to buffers with
|
|
// minimal allocation slop and then pushes them to the queue. This is
|
|
// useful for decoders working with formats that give awkward numbers of
|
|
// frames such as mp3.
|
|
AudioCompactor mAudioCompactor;
|
|
|
|
public:
|
|
// Populates aBuffered with the time ranges which are buffered. aStartTime
|
|
// must be the presentation time of the first frame in the media, e.g.
|
|
// the media time corresponding to playback time/position 0. This function
|
|
// is called on the main, decode, and state machine threads.
|
|
//
|
|
// This base implementation in MediaDecoderReader estimates the time ranges
|
|
// buffered by interpolating the cached byte ranges with the duration
|
|
// of the media. Reader subclasses should override this method if they
|
|
// can quickly calculate the buffered ranges more accurately.
|
|
//
|
|
// The primary advantage of this implementation in the reader base class
|
|
// is that it's a fast approximation, which does not perform any I/O.
|
|
//
|
|
// The OggReader relies on this base implementation not performing I/O,
|
|
// since in FirefoxOS we can't do I/O on the main thread, where this is
|
|
// called.
|
|
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
|
|
int64_t aStartTime);
|
|
|
|
// Returns the number of bytes of memory allocated by structures/frames in
|
|
// the video queue.
|
|
size_t SizeOfVideoQueueInBytes() const;
|
|
|
|
// Returns the number of bytes of memory allocated by structures/frames in
|
|
// the audio queue.
|
|
size_t SizeOfAudioQueueInBytes() const;
|
|
|
|
// Only used by WebMReader and MediaOmxReader for now, so stub here rather
|
|
// than in every reader than inherits from MediaDecoderReader.
|
|
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {}
|
|
|
|
virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
|
|
virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
|
|
|
|
// Returns a pointer to the decoder.
|
|
AbstractMediaDecoder* GetDecoder() {
|
|
return mDecoder;
|
|
}
|
|
|
|
AudioData* DecodeToFirstAudioData();
|
|
VideoData* DecodeToFirstVideoData();
|
|
|
|
// Decodes samples until we reach frames required to play at time aTarget
|
|
// (usecs). This also trims the samples to start exactly at aTarget,
|
|
// by discarding audio samples and adjusting start times of video frames.
|
|
nsresult DecodeToTarget(int64_t aTarget);
|
|
|
|
protected:
|
|
|
|
// Reference to the owning decoder object.
|
|
AbstractMediaDecoder* mDecoder;
|
|
|
|
// Stores presentation info required for playback.
|
|
MediaInfo mInfo;
|
|
|
|
// Whether we should accept media that we know we can't play
|
|
// directly, because they have a number of channel higher than
|
|
// what we support.
|
|
bool mIgnoreAudioOutputFormat;
|
|
};
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif
|