mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 07:05:24 +00:00
415 lines
16 KiB
C++
415 lines
16 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
|
|
/* 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/. */
|
|
|
|
/* DASH - Dynamic Adaptive Streaming over HTTP
|
|
*
|
|
* DASH is an adaptive bitrate streaming technology where a multimedia file is
|
|
* partitioned into one or more segments and delivered to a client using HTTP.
|
|
*
|
|
* see DASHDecoder.cpp for info on DASH interaction with the media engine.*/
|
|
|
|
#if !defined(DASHDecoder_h_)
|
|
#define DASHDecoder_h_
|
|
|
|
#include "nsTArray.h"
|
|
#include "nsIURI.h"
|
|
#include "nsITimer.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "MediaDecoder.h"
|
|
#include "DASHReader.h"
|
|
|
|
namespace mozilla {
|
|
namespace net {
|
|
class IMPDManager;
|
|
class nsDASHMPDParser;
|
|
class Representation;
|
|
}// net
|
|
|
|
class DASHRepDecoder;
|
|
|
|
class DASHDecoder : public MediaDecoder
|
|
{
|
|
public:
|
|
typedef class mozilla::net::IMPDManager IMPDManager;
|
|
typedef class mozilla::net::nsDASHMPDParser nsDASHMPDParser;
|
|
typedef class mozilla::net::Representation Representation;
|
|
|
|
// XXX Arbitrary max file size for MPD. 50MB seems generously large.
|
|
static const uint32_t DASH_MAX_MPD_SIZE = 50*1024*1024;
|
|
|
|
DASHDecoder();
|
|
~DASHDecoder();
|
|
|
|
MediaDecoder* Clone() MOZ_OVERRIDE {
|
|
if (!IsDASHEnabled()) {
|
|
return nullptr;
|
|
}
|
|
return new DASHDecoder();
|
|
}
|
|
|
|
// Creates a single state machine for all stream decoders.
|
|
// Called from Load on the main thread only.
|
|
MediaDecoderStateMachine* CreateStateMachine();
|
|
|
|
// Loads the MPD from the network and subsequently loads the media streams.
|
|
// Called from the main thread only.
|
|
nsresult Load(MediaResource* aResource,
|
|
nsIStreamListener** aListener,
|
|
MediaDecoder* aCloneDonor);
|
|
|
|
// Notifies download of MPD file has ended.
|
|
// Called on the main thread only.
|
|
void NotifyDownloadEnded(nsresult aStatus);
|
|
|
|
// Notification from |DASHReader| that a seek has occurred in
|
|
// |aSubsegmentIdx|. Passes notification onto subdecoder which downloaded
|
|
// the subsegment already, if download is in the past. Otherwise, it returns.
|
|
void NotifySeekInVideoSubsegment(int32_t aRepDecoderIdx,
|
|
int32_t aSubsegmentIdx);
|
|
void NotifySeekInAudioSubsegment(int32_t aSubsegmentIdx);
|
|
|
|
// Notifies that a byte range download has ended. As per the DASH spec, this
|
|
// allows for stream switching at the boundaries of the byte ranges.
|
|
// Called on the main thread only.
|
|
void NotifyDownloadEnded(DASHRepDecoder* aRepDecoder,
|
|
nsresult aStatus,
|
|
int32_t const aSubsegmentIdx);
|
|
|
|
// Notification from an |MediaDecoderReader| class that metadata has been
|
|
// read. Declared here to allow overloading.
|
|
void OnReadMetadataCompleted() MOZ_OVERRIDE { }
|
|
|
|
// Seeks to aTime in seconds
|
|
nsresult Seek(double aTime) MOZ_OVERRIDE;
|
|
|
|
// Notification from |DASHRepDecoder| that a metadata has been read.
|
|
// |DASHDecoder| will initiate load of data bytes for active audio/video
|
|
// decoders. Called on the decode thread.
|
|
void OnReadMetadataCompleted(DASHRepDecoder* aRepDecoder);
|
|
|
|
// Returns true if all subsegments from current decode position are
|
|
// downloaded. Must be in monitor. Call from any thread.
|
|
bool IsDataCachedToEndOfResource() MOZ_OVERRIDE;
|
|
|
|
// Refers to downloading data bytes, i.e. non metadata.
|
|
// Returns true if |aRepDecoder| is an active audio or video sub decoder AND
|
|
// if metadata for all audio or video decoders has been read.
|
|
// Could be called from any thread; enters decoder monitor.
|
|
bool IsDecoderAllowedToDownloadData(DASHRepDecoder* aRepDecoder);
|
|
|
|
// Refers to downloading data bytes during SEEKING.
|
|
// Returns true if |aRepDecoder| is the active audio sub decoder, OR if
|
|
// it is a video decoder and is allowed to download this subsegment.
|
|
// Returns false if there is still some metadata to download.
|
|
// Could be called from any thread; enters decoder monitor.
|
|
bool IsDecoderAllowedToDownloadSubsegment(DASHRepDecoder* aRepDecoder,
|
|
int32_t const aSubsegmentIdx);
|
|
|
|
// Determines if rep/sub decoders should be switched, and if so switches
|
|
// them. Notifies |DASHReader| if and when it should switch readers.
|
|
// Returns a pointer to the new active decoder.
|
|
// Called on the main thread.
|
|
nsresult PossiblySwitchDecoder(DASHRepDecoder* aRepDecoder);
|
|
|
|
// Sets the byte range index for audio|video downloads. Will only increment
|
|
// for current active decoders. Could be called from any thread.
|
|
// Requires monitor because of write to |mAudioSubsegmentIdx| or
|
|
// |mVideoSubsegmentIdx|.
|
|
void SetSubsegmentIndex(DASHRepDecoder* aRepDecoder,
|
|
int32_t aSubsegmentIdx);
|
|
|
|
// Suspend any media downloads that are in progress. Called by the
|
|
// media element when it is sent to the bfcache, or when we need
|
|
// to throttle the download. Call on the main thread only. This can
|
|
// be called multiple times, there's an internal "suspend count".
|
|
void Suspend() MOZ_OVERRIDE;
|
|
|
|
// Resume any media downloads that have been suspended. Called by the
|
|
// media element when it is restored from the bfcache, or when we need
|
|
// to stop throttling the download. Call on the main thread only.
|
|
// The download will only actually resume once as many Resume calls
|
|
// have been made as Suspend calls. When aForceBuffering is true,
|
|
// we force the decoder to go into buffering state before resuming
|
|
// playback.
|
|
void Resume(bool aForceBuffering) MOZ_OVERRIDE;
|
|
private:
|
|
// Increments the byte range index for audio|video downloads. Will only
|
|
// increment for current active decoders. Could be called from any thread.
|
|
// Requires monitor because of write to |mAudioSubsegmentIdx| or
|
|
// |mVideoSubsegmentIdx|.
|
|
void IncrementSubsegmentIndex(DASHRepDecoder* aRepDecoder)
|
|
{
|
|
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
|
if (aRepDecoder == AudioRepDecoder()) {
|
|
mAudioSubsegmentIdx++;
|
|
} else if (aRepDecoder == VideoRepDecoder()) {
|
|
mVideoSubsegmentIdx++;
|
|
}
|
|
}
|
|
public:
|
|
// Gets the byte range index for audio|video downloads. Will only increment
|
|
// for current active decoders. Could be called from any thread. Will enter
|
|
// monitor for read access off the decode thread.
|
|
int32_t GetSubsegmentIndex(DASHRepDecoder* aRepDecoder)
|
|
{
|
|
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
|
GetReentrantMonitor());
|
|
if (aRepDecoder == AudioRepDecoder()) {
|
|
return mAudioSubsegmentIdx;
|
|
} else if (aRepDecoder == VideoRepDecoder()) {
|
|
return mVideoSubsegmentIdx;
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
// Returns the total number of subsegments that have been loaded. Will enter
|
|
// monitor for read access off the decode thread.
|
|
uint32_t GetNumSubsegmentLoads() {
|
|
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
|
GetReentrantMonitor());
|
|
return mVideoSubsegmentLoads.Length();
|
|
}
|
|
|
|
// Returns the index of the rep decoder used to load a subsegment. Will enter
|
|
// monitor for read access off the decode thread.
|
|
int32_t GetRepIdxForVideoSubsegmentLoad(int32_t aSubsegmentIdx)
|
|
{
|
|
NS_ASSERTION(0 <= aSubsegmentIdx, "Subsegment index should not be negative.");
|
|
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
|
GetReentrantMonitor());
|
|
if ((uint32_t)aSubsegmentIdx < mVideoSubsegmentLoads.Length()) {
|
|
return mVideoSubsegmentLoads[aSubsegmentIdx];
|
|
} else {
|
|
// If it hasn't been downloaded yet, use the lowest bitrate decoder.
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Returns the index of the rep decoder used to load a subsegment, after a
|
|
// seek. Called on the decode thread, and will block if the subsegment
|
|
// previous to the one specified has not yet been loaded. This ensures that
|
|
// |DASHDecoder| has had a chance to determine which decoder should load the
|
|
// next subsegment, in the case where |DASHRepReader|::|DecodeToTarget| has
|
|
// read all the data for the current subsegment from the cache, and needs to
|
|
// know which reader (including itself) to use next.
|
|
int32_t GetRepIdxForVideoSubsegmentLoadAfterSeek(int32_t aSubsegmentIndex);
|
|
|
|
int32_t GetSwitchCountAtVideoSubsegment(int32_t aSubsegmentIdx)
|
|
{
|
|
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
|
GetReentrantMonitor());
|
|
NS_ASSERTION(0 <= aSubsegmentIdx, "Subsegment index should not be negative.");
|
|
if (aSubsegmentIdx == 0) {
|
|
// Do the zeroeth switch next.
|
|
return 0;
|
|
}
|
|
int32_t switchCount = 0;
|
|
for (uint32_t i = 1;
|
|
i < mVideoSubsegmentLoads.Length() &&
|
|
i <= (uint32_t)aSubsegmentIdx;
|
|
i++) {
|
|
if (mVideoSubsegmentLoads[i-1] != mVideoSubsegmentLoads[i]) {
|
|
switchCount++;
|
|
}
|
|
}
|
|
return switchCount;
|
|
}
|
|
|
|
// The actual playback rate computation. The monitor must be held.
|
|
// XXX Computes playback for the current video rep decoder only.
|
|
double ComputePlaybackRate(bool* aReliable) MOZ_OVERRIDE;
|
|
|
|
// Something has changed that could affect the computed playback rate,
|
|
// so recompute it. The monitor must be held. Will be forwarded to current
|
|
// audio and video rep decoders.
|
|
void UpdatePlaybackRate() MOZ_OVERRIDE;
|
|
|
|
// Stop updating the bytes downloaded for progress notifications. Called
|
|
// when seeking to prevent wild changes to the progress notification.
|
|
// Forwarded to sub-decoders. Must be called with the decoder monitor held.
|
|
void StopProgressUpdates() MOZ_OVERRIDE;
|
|
|
|
// Allow updating the bytes downloaded for progress notifications.
|
|
// Forwarded to sub-decoders. Must be called with the decoder monitor held.
|
|
void StartProgressUpdates() MOZ_OVERRIDE;
|
|
|
|
// Used to estimate rates of data passing through the decoder's channel.
|
|
// Records activity starting on the channel. The monitor must be held.
|
|
virtual void NotifyPlaybackStarted() MOZ_OVERRIDE;
|
|
|
|
// Used to estimate rates of data passing through the decoder's channel.
|
|
// Records activity stopping on the channel. The monitor must be held.
|
|
virtual void NotifyPlaybackStopped() MOZ_OVERRIDE;
|
|
|
|
// Return statistics. This is used for progress events and other things.
|
|
// This can be called from any thread. It's only a snapshot of the
|
|
// current state, since other threads might be changing the state
|
|
// at any time.
|
|
// XXX Stats are calculated based on the current video rep decoder, with the
|
|
// exception of download rate, which is based on all video downloads.
|
|
virtual Statistics GetStatistics() MOZ_OVERRIDE;
|
|
|
|
// Drop reference to state machine and tell sub-decoders to do the same.
|
|
// Only called during shutdown dance, on main thread only.
|
|
void ReleaseStateMachine();
|
|
|
|
// Overridden to forward |Shutdown| to sub-decoders.
|
|
// Called on the main thread only.
|
|
void Shutdown();
|
|
|
|
// Called by sub-decoders when load has been aborted. Will notify media
|
|
// element only once. Called on the main thread only.
|
|
void LoadAborted();
|
|
|
|
// Notifies the element that decoding has failed. On main thread, call is
|
|
// forwarded to |MediaDecoder|::|Error| immediately. On other threads,
|
|
// a call is dispatched for execution on the main thread.
|
|
void DecodeError();
|
|
|
|
private:
|
|
// Reads the MPD data from resource to a byte stream.
|
|
// Called on the MPD reader thread.
|
|
void ReadMPDBuffer();
|
|
|
|
// Called when MPD data is completely read.
|
|
// On the main thread.
|
|
void OnReadMPDBufferCompleted();
|
|
|
|
// Parses the copied MPD byte stream.
|
|
// On the main thread: DOM APIs complain when off the main thread.
|
|
nsresult ParseMPDBuffer();
|
|
|
|
// Creates the sub-decoders for a |Representation|, i.e. media streams.
|
|
// On the main thread.
|
|
nsresult CreateRepDecoders();
|
|
|
|
// Creates audio/video decoders for individual |Representation|s.
|
|
// On the main thread.
|
|
nsresult CreateAudioRepDecoder(nsIURI* aUrl, Representation const * aRep);
|
|
nsresult CreateVideoRepDecoder(nsIURI* aUrl, Representation const * aRep);
|
|
|
|
// Get audio sub-decoder for current audio |Representation|. Will return
|
|
// nullptr for out of range indexes.
|
|
// Enters monitor for read access off the decode thread.
|
|
// XXX Note: Although an array of audio decoders is provided, audio stream
|
|
// switching is not yet supported.
|
|
DASHRepDecoder* AudioRepDecoder() {
|
|
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
|
GetReentrantMonitor());
|
|
if (0 == mAudioRepDecoders.Length()) {
|
|
return nullptr;
|
|
}
|
|
NS_ENSURE_TRUE((uint32_t)mAudioRepDecoderIdx < mAudioRepDecoders.Length(),
|
|
nullptr);
|
|
if (mAudioRepDecoderIdx < 0) {
|
|
return nullptr;
|
|
} else {
|
|
return mAudioRepDecoders[mAudioRepDecoderIdx];
|
|
}
|
|
}
|
|
|
|
// Get video sub-decoder for current video |Representation|. Will return
|
|
// nullptr for out of range indexes.
|
|
// Enters monitor for read access off the decode thread.
|
|
DASHRepDecoder* VideoRepDecoder() {
|
|
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
|
GetReentrantMonitor());
|
|
if (0 == mVideoRepDecoders.Length()) {
|
|
return nullptr;
|
|
}
|
|
NS_ENSURE_TRUE((uint32_t)mVideoRepDecoderIdx < mVideoRepDecoders.Length(),
|
|
nullptr);
|
|
if (mVideoRepDecoderIdx < 0) {
|
|
return nullptr;
|
|
} else {
|
|
return mVideoRepDecoders[mVideoRepDecoderIdx];
|
|
}
|
|
}
|
|
|
|
// Creates audio/video resources for individual |Representation|s.
|
|
// On the main thread.
|
|
MediaResource* CreateAudioSubResource(nsIURI* aUrl,
|
|
MediaDecoder* aAudioDecoder);
|
|
MediaResource* CreateVideoSubResource(nsIURI* aUrl,
|
|
MediaDecoder* aVideoDecoder);
|
|
|
|
// Creates an http channel for a |Representation|.
|
|
// On the main thread.
|
|
nsresult CreateSubChannel(nsIURI* aUrl, nsIChannel** aChannel);
|
|
|
|
// Loads the media |Representations|, i.e. the media streams.
|
|
// On the main thread.
|
|
nsresult LoadRepresentations();
|
|
|
|
// True when media element has already been notified of an aborted load.
|
|
bool mNotifiedLoadAborted;
|
|
|
|
// Ptr for the MPD data.
|
|
nsAutoArrayPtr<char> mBuffer;
|
|
// Length of the MPD data.
|
|
uint32_t mBufferLength;
|
|
// Ptr to the MPD Reader thread.
|
|
nsCOMPtr<nsIThread> mMPDReaderThread;
|
|
// Document Principal.
|
|
nsCOMPtr<nsIPrincipal> mPrincipal;
|
|
|
|
// MPD Manager provides access to the MPD information.
|
|
nsAutoPtr<IMPDManager> mMPDManager;
|
|
|
|
// Main reader object; manages all sub-readers for |Representation|s. Owned by
|
|
// state machine; destroyed in state machine's destructor.
|
|
DASHReader* mDASHReader;
|
|
|
|
// Sub-decoder vars. Note: For all following members, the decode monitor
|
|
// should be held for write access on decode thread, and all read/write off
|
|
// the decode thread.
|
|
|
|
// Index of the video |AdaptationSet|.
|
|
int32_t mVideoAdaptSetIdx;
|
|
|
|
// Indexes for the current audio and video decoders.
|
|
int32_t mAudioRepDecoderIdx;
|
|
int32_t mVideoRepDecoderIdx;
|
|
|
|
// Array of pointers for the |Representation|s in the audio/video
|
|
// |AdaptationSet|.
|
|
nsTArray<nsRefPtr<DASHRepDecoder> > mAudioRepDecoders;
|
|
nsTArray<nsRefPtr<DASHRepDecoder> > mVideoRepDecoders;
|
|
|
|
// Current index of subsegments downloaded for audio/video decoder.
|
|
int32_t mAudioSubsegmentIdx;
|
|
int32_t mVideoSubsegmentIdx;
|
|
|
|
// Count for the number of readers which have called |OnReadMetadataCompleted|.
|
|
// Initialised to 0; incremented for every decoder which has |Load| called;
|
|
// and decremented for every call to |OnReadMetadataCompleted|. When it is
|
|
// zero again, all metadata has been read for audio or video, and data bytes
|
|
// can be downloaded.
|
|
uint32_t mAudioMetadataReadCount;
|
|
uint32_t mVideoMetadataReadCount;
|
|
|
|
// Array records the index of the decoder/Representation which loaded each
|
|
// subsegment.
|
|
nsTArray<int32_t> mVideoSubsegmentLoads;
|
|
|
|
// True when Seek is called; will block any downloads until
|
|
// |NotifySeekInSubsegment| is called, which will set it to false, and will
|
|
// start a new series of downloads from the seeked subsegment.
|
|
bool mSeeking;
|
|
|
|
// Mutex for statistics.
|
|
Mutex mStatisticsLock;
|
|
// Stores snapshot statistics, such as download rate, for the audio|video
|
|
// data streams. |mStatisticsLock| must be locked for access.
|
|
nsRefPtr<MediaChannelStatistics> mAudioStatistics;
|
|
nsRefPtr<MediaChannelStatistics> mVideoStatistics;
|
|
};
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif
|