mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 935349 - Remove disabled DASH code. r=doublec
This commit is contained in:
parent
1dffedf3df
commit
5ccd288bd0
@ -3929,7 +3929,6 @@ MOZ_SAMPLE_TYPE_FLOAT32=
|
||||
MOZ_SAMPLE_TYPE_S16=
|
||||
MOZ_OPUS=1
|
||||
MOZ_WEBM=1
|
||||
MOZ_DASH=
|
||||
MOZ_DIRECTSHOW=
|
||||
MOZ_WMF=
|
||||
MOZ_WEBRTC=1
|
||||
@ -8633,7 +8632,6 @@ AC_SUBST(MOZ_VORBIS)
|
||||
AC_SUBST(MOZ_TREMOR)
|
||||
AC_SUBST(MOZ_OPUS)
|
||||
AC_SUBST(MOZ_WEBM)
|
||||
AC_SUBST(MOZ_DASH)
|
||||
AC_SUBST(MOZ_WMF)
|
||||
AC_SUBST(MOZ_DIRECTSHOW)
|
||||
AC_SUBST(MOZ_MEDIA_PLUGINS)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,412 +0,0 @@
|
||||
/* -*- 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 "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.
|
||||
virtual nsresult Load(nsIStreamListener** aListener,
|
||||
MediaDecoder* aCloneDonor) MOZ_OVERRIDE;
|
||||
|
||||
// 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
|
@ -1,674 +0,0 @@
|
||||
/* -*- 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.*/
|
||||
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "VideoFrameContainer.h"
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "DASHReader.h"
|
||||
#include "DASHDecoder.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* gDASHReaderLog;
|
||||
#define LOG(msg, ...) PR_LOG(gDASHReaderLog, PR_LOG_DEBUG, \
|
||||
("%p [DASHReader] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) PR_LOG(gDASHReaderLog, PR_LOG_DEBUG, \
|
||||
("%p [DASHReader] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
DASHReader::DASHReader(AbstractMediaDecoder* aDecoder) :
|
||||
MediaDecoderReader(aDecoder),
|
||||
mReadMetadataMonitor("media.dashreader.readmetadata"),
|
||||
mReadyToReadMetadata(false),
|
||||
mDecoderIsShuttingDown(false),
|
||||
mAudioReader(this),
|
||||
mVideoReader(this),
|
||||
mAudioReaders(this),
|
||||
mVideoReaders(this),
|
||||
mSwitchVideoReaders(false),
|
||||
mSwitchCount(-1)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DASHReader);
|
||||
#ifdef PR_LOGGING
|
||||
if (!gDASHReaderLog) {
|
||||
gDASHReaderLog = PR_NewLogModule("DASHReader");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DASHReader::~DASHReader()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DASHReader);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::ResetDecode()
|
||||
{
|
||||
MediaDecoderReader::ResetDecode();
|
||||
nsresult rv;
|
||||
for (uint i = 0; i < mAudioReaders.Length(); i++) {
|
||||
rv = mAudioReaders[i]->ResetDecode();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
for (uint i = 0; i < mVideoReaders.Length(); i++) {
|
||||
rv = mVideoReaders[i]->ResetDecode();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(mAudioReaders.Length() != 0 && mVideoReaders.Length() != 0,
|
||||
"Audio and video readers should exist already.");
|
||||
|
||||
nsresult rv;
|
||||
for (uint i = 0; i < mAudioReaders.Length(); i++) {
|
||||
rv = mAudioReaders[i]->Init(nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
for (uint i = 0; i < mVideoReaders.Length(); i++) {
|
||||
rv = mVideoReaders[i]->Init(nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::AddAudioReader(DASHRepReader* aAudioReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE_VOID(aAudioReader);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
mAudioReaders.AppendElement(aAudioReader);
|
||||
// XXX For now, just pick the first reader to be default.
|
||||
if (!mAudioReader)
|
||||
mAudioReader = aAudioReader;
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::AddVideoReader(DASHRepReader* aVideoReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE_VOID(aVideoReader);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
mVideoReaders.AppendElement(aVideoReader);
|
||||
// XXX For now, just pick the first reader to be default.
|
||||
if (!mVideoReader)
|
||||
mVideoReader = aVideoReader;
|
||||
}
|
||||
|
||||
bool
|
||||
DASHReader::HasAudio()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
return mAudioReader ? mAudioReader->HasAudio() : false;
|
||||
}
|
||||
|
||||
bool
|
||||
DASHReader::HasVideo()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
return mVideoReader ? mVideoReader->HasVideo() : false;
|
||||
}
|
||||
|
||||
int64_t
|
||||
DASHReader::VideoQueueMemoryInUse()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
return VideoQueueMemoryInUse();
|
||||
}
|
||||
|
||||
int64_t
|
||||
DASHReader::AudioQueueMemoryInUse()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
return AudioQueueMemoryInUse();
|
||||
}
|
||||
|
||||
bool
|
||||
DASHReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
if (mVideoReader) {
|
||||
return mVideoReader->DecodeVideoFrame(aKeyframeSkip, aTimeThreshold);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DASHReader::DecodeAudioData()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
return (mAudioReader ? mAudioReader->DecodeAudioData() : false);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// Wait for MPD to be parsed and child readers created.
|
||||
LOG1("Waiting for metadata download.");
|
||||
nsresult rv = WaitForMetadata();
|
||||
// If we get an abort, return silently; the decoder is shutting down.
|
||||
if (NS_ERROR_ABORT == rv) {
|
||||
return NS_OK;
|
||||
}
|
||||
// Verify no other errors before continuing.
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(aTags, "Called with null MetadataTags**.");
|
||||
*aTags = nullptr;
|
||||
|
||||
// Get metadata from child readers.
|
||||
MediaInfo audioInfo, videoInfo;
|
||||
|
||||
// Read metadata for all video streams.
|
||||
for (uint i = 0; i < mVideoReaders.Length(); i++) {
|
||||
// Use an nsAutoPtr here to ensure |tags| memory does not leak.
|
||||
nsAutoPtr<HTMLMediaElement::MetadataTags> tags;
|
||||
rv = mVideoReaders[i]->ReadMetadata(&videoInfo, getter_Transfers(tags));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Use metadata from current video sub reader to populate aInfo.
|
||||
if (mVideoReaders[i] == mVideoReader) {
|
||||
mInfo.mVideo = videoInfo.mVideo;
|
||||
}
|
||||
}
|
||||
// Read metadata for audio stream.
|
||||
// Note: Getting metadata tags from audio reader only for now.
|
||||
// XXX Audio stream switching not yet supported.
|
||||
if (mAudioReader) {
|
||||
rv = mAudioReader->ReadMetadata(&audioInfo, aTags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mInfo.mAudio = audioInfo.mAudio;
|
||||
}
|
||||
|
||||
*aInfo = mInfo;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::Seek(int64_t aTime,
|
||||
int64_t aStartTime,
|
||||
int64_t aEndTime,
|
||||
int64_t aCurrentTime)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
NS_ENSURE_SUCCESS(ResetDecode(), NS_ERROR_FAILURE);
|
||||
|
||||
LOG("Seeking to [%.2fs]", aTime/1000000.0);
|
||||
|
||||
nsresult rv;
|
||||
DASHDecoder* dashDecoder = static_cast<DASHDecoder*>(mDecoder);
|
||||
|
||||
if (mAudioReader) {
|
||||
int64_t subsegmentIdx = -1;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
subsegmentIdx = mAudioReader->GetSubsegmentForSeekTime(aTime);
|
||||
NS_ENSURE_TRUE(0 <= subsegmentIdx, NS_ERROR_ILLEGAL_VALUE);
|
||||
}
|
||||
dashDecoder->NotifySeekInAudioSubsegment(subsegmentIdx);
|
||||
|
||||
rv = mAudioReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (mVideoReader) {
|
||||
// Determine the video subsegment we're seeking to.
|
||||
int32_t subsegmentIdx = -1;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
subsegmentIdx = mVideoReader->GetSubsegmentForSeekTime(aTime);
|
||||
NS_ENSURE_TRUE(0 <= subsegmentIdx, NS_ERROR_ILLEGAL_VALUE);
|
||||
}
|
||||
|
||||
LOG("Seek to [%.2fs] found in video subsegment [%d]",
|
||||
aTime/1000000.0, subsegmentIdx);
|
||||
|
||||
// Determine if/which video reader previously downloaded this subsegment.
|
||||
int32_t readerIdx = dashDecoder->GetRepIdxForVideoSubsegmentLoad(subsegmentIdx);
|
||||
|
||||
dashDecoder->NotifySeekInVideoSubsegment(readerIdx, subsegmentIdx);
|
||||
|
||||
if (0 <= readerIdx) {
|
||||
NS_ENSURE_TRUE(readerIdx < mVideoReaders.Length(),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
// Switch to this reader and do the Seek.
|
||||
DASHRepReader* fromReader = mVideoReader;
|
||||
DASHRepReader* toReader = mVideoReaders[readerIdx];
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (fromReader != toReader) {
|
||||
LOG("Switching video readers now from [%p] to [%p] for a seek to "
|
||||
"[%.2fs] in subsegment [%d]",
|
||||
fromReader, toReader, aTime/1000000.0, subsegmentIdx);
|
||||
|
||||
mVideoReader = toReader;
|
||||
}
|
||||
}
|
||||
|
||||
rv = mVideoReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Go back to the appropriate count in the switching history, and setup
|
||||
// this main reader and the sub readers for the next switch (if any).
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mSwitchCount = dashDecoder->GetSwitchCountAtVideoSubsegment(subsegmentIdx);
|
||||
LOG("After mVideoReader->Seek() mSwitchCount %d", mSwitchCount);
|
||||
NS_ENSURE_TRUE(0 <= mSwitchCount, NS_ERROR_ILLEGAL_VALUE);
|
||||
NS_ENSURE_TRUE(mSwitchCount <= subsegmentIdx, NS_ERROR_ILLEGAL_VALUE);
|
||||
}
|
||||
} else {
|
||||
LOG("Error getting rep idx for video subsegment [%d]",
|
||||
subsegmentIdx);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::GetBuffered(TimeRanges* aBuffered,
|
||||
int64_t aStartTime)
|
||||
{
|
||||
NS_ENSURE_ARG(aBuffered);
|
||||
|
||||
MediaResource* resource = nullptr;
|
||||
AbstractMediaDecoder* decoder = nullptr;
|
||||
|
||||
TimeRanges audioBuffered, videoBuffered;
|
||||
uint32_t audioRangeCount = 0, videoRangeCount = 0;
|
||||
bool audioCachedAtEnd = false, videoCachedAtEnd = false;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Get all audio and video buffered ranges. Include inactive streams, since
|
||||
// we may have carried out a seek and future subsegments may be in currently
|
||||
// inactive decoders.
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
for (uint32_t i = 0; i < mAudioReaders.Length(); i++) {
|
||||
decoder = mAudioReaders[i]->GetDecoder();
|
||||
NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
|
||||
resource = decoder->GetResource();
|
||||
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
|
||||
resource->Pin();
|
||||
rv = mAudioReaders[i]->GetBuffered(&audioBuffered, aStartTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// If data was cached at the end, then the final timestamp refers to the
|
||||
// end of the data. Use this later to extend end time if necessary.
|
||||
if (!audioCachedAtEnd) {
|
||||
audioCachedAtEnd = mAudioReaders[i]->IsDataCachedAtEndOfSubsegments();
|
||||
}
|
||||
resource->Unpin();
|
||||
}
|
||||
for (uint32_t i = 0; i < mVideoReaders.Length(); i++) {
|
||||
decoder = mVideoReaders[i]->GetDecoder();
|
||||
NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
|
||||
resource = decoder->GetResource();
|
||||
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
|
||||
resource->Pin();
|
||||
rv = mVideoReaders[i]->GetBuffered(&videoBuffered, aStartTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// If data was cached at the end, then the final timestamp refers to the
|
||||
// end of the data. Use this later to extend end time if necessary.
|
||||
if (!videoCachedAtEnd) {
|
||||
videoCachedAtEnd = mVideoReaders[i]->IsDataCachedAtEndOfSubsegments();
|
||||
}
|
||||
resource->Unpin();
|
||||
}
|
||||
|
||||
audioBuffered.Normalize();
|
||||
videoBuffered.Normalize();
|
||||
|
||||
rv = audioBuffered.GetLength(&audioRangeCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = videoBuffered.GetLength(&videoRangeCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
double start = 0, end = 0;
|
||||
for (uint32_t i = 0; i < audioRangeCount; i++) {
|
||||
rv = audioBuffered.Start(i, &start);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = audioBuffered.End(i, &end);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
LOG("audioBuffered[%d] = (%f, %f)",
|
||||
i, start, end);
|
||||
}
|
||||
for (uint32_t i = 0; i < videoRangeCount; i++) {
|
||||
rv = videoBuffered.Start(i, &start);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = videoBuffered.End(i, &end);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
LOG("videoBuffered[%d] = (%f, %f)",
|
||||
i, start, end);
|
||||
}
|
||||
#endif
|
||||
|
||||
// If audio and video are cached to the end of their subsegments, extend the
|
||||
// end time of the shorter of the two. Presentation of the shorter stream
|
||||
// will stop at the end, while the other continues until the combined
|
||||
// playback is complete.
|
||||
// Note: Only in cases where the shorter stream is fully cached, and the
|
||||
// longer stream is partially cached, but with more time buffered than the
|
||||
// shorter stream.
|
||||
//
|
||||
// Audio ========|
|
||||
// 20
|
||||
// Video ============|----|
|
||||
// 30 40
|
||||
// Combo ============| <----- End time EXTENDED.
|
||||
//
|
||||
// For example, audio is fully cached to 20s, but video is partially cached
|
||||
// to 30s, full duration 40s. In this case, the buffered end time should be
|
||||
// extended to the video's end time.
|
||||
//
|
||||
// Audio =================|
|
||||
// 40
|
||||
// Video ========|----|
|
||||
// 20 30
|
||||
// Combo ========| <------ End time NOT EXTENDED.
|
||||
//
|
||||
// Conversely, if the longer stream is fully cached, but the shorter one is
|
||||
// not, no extension of end time should occur - we should consider the
|
||||
// partially cached, shorter end time to be the end time of the combined
|
||||
// stream
|
||||
|
||||
if (audioCachedAtEnd || videoCachedAtEnd) {
|
||||
NS_ENSURE_TRUE(audioRangeCount, NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(videoRangeCount, NS_ERROR_FAILURE);
|
||||
|
||||
double audioEndTime = 0, videoEndTime = 0;
|
||||
// Get end time of the last range of buffered audio.
|
||||
audioEndTime = audioBuffered.GetFinalEndTime();
|
||||
NS_ENSURE_TRUE(audioEndTime > 0, NS_ERROR_ILLEGAL_VALUE);
|
||||
// Get end time of the last range of buffered video.
|
||||
videoEndTime = videoBuffered.GetFinalEndTime();
|
||||
NS_ENSURE_TRUE(videoEndTime > 0, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// API for TimeRanges requires extending through adding and normalizing.
|
||||
if (videoCachedAtEnd && audioEndTime > videoEndTime) {
|
||||
videoBuffered.Add(videoEndTime, audioEndTime);
|
||||
videoBuffered.Normalize();
|
||||
LOG("videoBuffered extended to %f", audioEndTime);
|
||||
} else if (audioCachedAtEnd && videoEndTime > audioEndTime) {
|
||||
audioBuffered.Add(audioEndTime, videoEndTime);
|
||||
audioBuffered.Normalize();
|
||||
LOG("audioBuffered extended to %f", videoEndTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate intersecting ranges for video and audio.
|
||||
if (!mAudioReaders.IsEmpty() && !mVideoReaders.IsEmpty()) {
|
||||
for (uint32_t i = 0; i < audioRangeCount; i++) {
|
||||
// |A|udio, |V|ideo, |I|ntersect.
|
||||
double startA, startV, startI;
|
||||
double endA, endV, endI;
|
||||
rv = audioBuffered.Start(i, &startA);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = audioBuffered.End(i, &endA);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (uint32_t j = 0; j < videoRangeCount; j++) {
|
||||
rv = videoBuffered.Start(i, &startV);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = videoBuffered.End(i, &endV);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If video block is before audio block, compare next video block.
|
||||
if (startA > endV) {
|
||||
continue;
|
||||
// If video block is after audio block, all of them are; compare next
|
||||
// audio block.
|
||||
} else if (endA < startV) {
|
||||
break;
|
||||
}
|
||||
// Calculate intersections of current audio and video blocks.
|
||||
startI = (startA > startV) ? startA : startV;
|
||||
endI = (endA > endV) ? endV : endA;
|
||||
aBuffered->Add(startI, endI);
|
||||
}
|
||||
}
|
||||
} else if (!mAudioReaders.IsEmpty()) {
|
||||
*aBuffered = audioBuffered;
|
||||
} else if (!mVideoReaders.IsEmpty()) {
|
||||
*aBuffered = videoBuffered;
|
||||
} else {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
VideoData*
|
||||
DASHReader::FindStartTime(int64_t& aOutStartTime)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
|
||||
"Should be on state machine or decode thread.");
|
||||
|
||||
// Extract the start times of the bitstreams in order to calculate
|
||||
// the duration.
|
||||
int64_t videoStartTime = INT64_MAX;
|
||||
int64_t audioStartTime = INT64_MAX;
|
||||
VideoData* videoData = nullptr;
|
||||
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
if (HasVideo()) {
|
||||
// Forward to video reader.
|
||||
videoData = mVideoReader->DecodeToFirstVideoData();
|
||||
if (videoData) {
|
||||
videoStartTime = videoData->mTime;
|
||||
}
|
||||
}
|
||||
if (HasAudio()) {
|
||||
// Forward to audio reader.
|
||||
AudioData* audioData = mAudioReader->DecodeToFirstAudioData();
|
||||
if (audioData) {
|
||||
audioStartTime = audioData->mTime;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t startTime = std::min(videoStartTime, audioStartTime);
|
||||
if (startTime != INT64_MAX) {
|
||||
aOutStartTime = startTime;
|
||||
}
|
||||
|
||||
return videoData;
|
||||
}
|
||||
|
||||
MediaQueue<AudioData>&
|
||||
DASHReader::AudioQueue()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(mAudioReader, "mAudioReader is NULL!");
|
||||
return mAudioQueue;
|
||||
}
|
||||
|
||||
MediaQueue<VideoData>&
|
||||
DASHReader::VideoQueue()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(mVideoReader, "mVideoReader is NULL!");
|
||||
return mVideoQueue;
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::RequestVideoReaderSwitch(uint32_t aFromReaderIdx,
|
||||
uint32_t aToReaderIdx,
|
||||
uint32_t aSubsegmentIdx)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ASSERTION(aFromReaderIdx < mVideoReaders.Length(),
|
||||
"From index is greater than number of video readers!");
|
||||
NS_ASSERTION(aToReaderIdx < mVideoReaders.Length(),
|
||||
"To index is greater than number of video readers!");
|
||||
NS_ASSERTION(aToReaderIdx != aFromReaderIdx,
|
||||
"Don't request switches to same reader!");
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
if (mSwitchCount < 0) {
|
||||
mSwitchCount = 0;
|
||||
}
|
||||
|
||||
DASHRepReader* fromReader = mVideoReaders[aFromReaderIdx];
|
||||
DASHRepReader* toReader = mVideoReaders[aToReaderIdx];
|
||||
|
||||
LOG("Switch requested from reader [%d] [%p] to reader [%d] [%p] "
|
||||
"at subsegment[%d].",
|
||||
aFromReaderIdx, fromReader, aToReaderIdx, toReader, aSubsegmentIdx);
|
||||
|
||||
// Append the subsegment index to the list of pending switches.
|
||||
for (uint32_t i = 0; i < mSwitchToVideoSubsegmentIndexes.Length(); i++) {
|
||||
if (mSwitchToVideoSubsegmentIndexes[i] == aSubsegmentIdx) {
|
||||
// A backwards |Seek| has changed the switching history; delete from
|
||||
// this point on.
|
||||
mSwitchToVideoSubsegmentIndexes.TruncateLength(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mSwitchToVideoSubsegmentIndexes.AppendElement(aSubsegmentIdx);
|
||||
|
||||
// Tell the SWITCH FROM reader when it should stop reading.
|
||||
fromReader->RequestSwitchAtSubsegment(aSubsegmentIdx, toReader);
|
||||
|
||||
// Tell the SWITCH TO reader to seek to the correct offset.
|
||||
toReader->RequestSeekToSubsegment(aSubsegmentIdx);
|
||||
|
||||
mSwitchVideoReaders = true;
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::PossiblySwitchVideoReaders()
|
||||
{
|
||||
NS_ASSERTION(mDecoder, "Decoder should not be null");
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// Flag to switch streams is set in |RequestVideoReaderSwitch|.
|
||||
if (!mSwitchVideoReaders) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only switch if we reached a switch access point.
|
||||
NS_ENSURE_TRUE_VOID(0 <= mSwitchCount);
|
||||
NS_ENSURE_TRUE_VOID((uint32_t)mSwitchCount < mSwitchToVideoSubsegmentIndexes.Length());
|
||||
uint32_t switchIdx = mSwitchToVideoSubsegmentIndexes[mSwitchCount];
|
||||
if (!mVideoReader->HasReachedSubsegment(switchIdx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Representation index to switch to.
|
||||
DASHDecoder* dashDecoder = static_cast<DASHDecoder*>(mDecoder);
|
||||
int32_t toReaderIdx = dashDecoder->GetRepIdxForVideoSubsegmentLoad(switchIdx);
|
||||
NS_ENSURE_TRUE_VOID(0 <= toReaderIdx);
|
||||
NS_ENSURE_TRUE_VOID((uint32_t)toReaderIdx < mVideoReaders.Length());
|
||||
|
||||
DASHRepReader* fromReader = mVideoReader;
|
||||
DASHRepReader* toReader = mVideoReaders[toReaderIdx];
|
||||
NS_ENSURE_TRUE_VOID(fromReader != toReader);
|
||||
|
||||
LOG("Switching video readers now from [%p] to [%p] at subsegment [%d]: "
|
||||
"mSwitchCount [%d].",
|
||||
fromReader, toReader, switchIdx, mSwitchCount);
|
||||
|
||||
// Switch readers while in the monitor.
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mVideoReader = toReader;
|
||||
|
||||
// Prep readers for next switch, also while in monitor.
|
||||
if ((uint32_t)++mSwitchCount < mSwitchToVideoSubsegmentIndexes.Length()) {
|
||||
// Get the subsegment at which to switch.
|
||||
switchIdx = mSwitchToVideoSubsegmentIndexes[mSwitchCount];
|
||||
|
||||
// Update from and to reader ptrs for next switch.
|
||||
fromReader = toReader;
|
||||
toReaderIdx = dashDecoder->GetRepIdxForVideoSubsegmentLoad(switchIdx);
|
||||
toReader = mVideoReaders[toReaderIdx];
|
||||
NS_ENSURE_TRUE_VOID((uint32_t)toReaderIdx < mVideoReaders.Length());
|
||||
NS_ENSURE_TRUE_VOID(fromReader != toReader);
|
||||
|
||||
// Tell the SWITCH FROM reader when it should stop reading.
|
||||
fromReader->RequestSwitchAtSubsegment(switchIdx, toReader);
|
||||
|
||||
// Tell the SWITCH TO reader to seek to the correct offset.
|
||||
toReader->RequestSeekToSubsegment(switchIdx);
|
||||
} else {
|
||||
// If there are no more pending switches, unset the switch readers flag.
|
||||
mSwitchVideoReaders = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::PrepareToDecode()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// Flag to switch streams is set by |DASHDecoder|.
|
||||
if (!mSwitchVideoReaders) {
|
||||
return;
|
||||
}
|
||||
|
||||
PossiblySwitchVideoReaders();
|
||||
|
||||
// Prepare each sub reader for decoding: includes seeking to the correct
|
||||
// offset if a seek was previously requested.
|
||||
for (uint32_t i = 0; i < mVideoReaders.Length(); i++) {
|
||||
mVideoReaders[i]->PrepareToDecode();
|
||||
}
|
||||
}
|
||||
|
||||
DASHRepReader*
|
||||
DASHReader::GetReaderForSubsegment(uint32_t aSubsegmentIdx)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
DASHDecoder* dashDecoder = static_cast<DASHDecoder*>(mDecoder);
|
||||
int32_t repIdx =
|
||||
dashDecoder->GetRepIdxForVideoSubsegmentLoadAfterSeek((int32_t)aSubsegmentIdx);
|
||||
if (0 <= repIdx && repIdx < mVideoReaders.Length()) {
|
||||
return mVideoReaders[repIdx];
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace mozilla
|
@ -1,303 +0,0 @@
|
||||
/* -*- 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 comments on DASH object interaction
|
||||
*/
|
||||
|
||||
#if !defined(DASHReader_h_)
|
||||
#define DASHReader_h_
|
||||
|
||||
#include "VideoUtils.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "DASHRepReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DASHRepReader;
|
||||
|
||||
class DASHReader : public MediaDecoderReader
|
||||
{
|
||||
public:
|
||||
DASHReader(AbstractMediaDecoder* aDecoder);
|
||||
~DASHReader();
|
||||
nsresult ResetDecode() MOZ_OVERRIDE;
|
||||
|
||||
// Adds a pointer to a audio/video reader for a media |Representation|.
|
||||
// Called on the main thread only.
|
||||
void AddAudioReader(DASHRepReader* aAudioReader);
|
||||
void AddVideoReader(DASHRepReader* aVideoReader);
|
||||
|
||||
// Waits for metadata bytes to be downloaded, then reads and parses them.
|
||||
// Called on the decode thread only.
|
||||
nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) MOZ_OVERRIDE;
|
||||
|
||||
// Waits for |ReadyToReadMetadata| or |NotifyDecoderShuttingDown|
|
||||
// notification, whichever comes first. Ensures no attempt to read metadata
|
||||
// during |DASHDecoder|::|Shutdown|. Called on decode thread only.
|
||||
nsresult WaitForMetadata() {
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
ReentrantMonitorAutoEnter mon(mReadMetadataMonitor);
|
||||
while (true) {
|
||||
// Abort if the decoder has started shutting down.
|
||||
if (mDecoderIsShuttingDown) {
|
||||
return NS_ERROR_ABORT;
|
||||
} else if (mReadyToReadMetadata) {
|
||||
break;
|
||||
}
|
||||
mon.Wait();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Called on the main thread by |DASHDecoder| to notify that metadata bytes
|
||||
// have been downloaded.
|
||||
void ReadyToReadMetadata() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
ReentrantMonitorAutoEnter mon(mReadMetadataMonitor);
|
||||
mReadyToReadMetadata = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
// Called on the main thread by |DASHDecoder| when it starts Shutdown. Will
|
||||
// wake metadata monitor if waiting for a silent return from |ReadMetadata|.
|
||||
void NotifyDecoderShuttingDown() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
ReentrantMonitorAutoEnter metadataMon(mReadMetadataMonitor);
|
||||
mDecoderIsShuttingDown = true;
|
||||
// Notify |ReadMetadata| of the shutdown if it's waiting.
|
||||
metadataMon.NotifyAll();
|
||||
}
|
||||
|
||||
// Audio/video status are dependent on the presence of audio/video readers.
|
||||
// Call on decode thread only.
|
||||
bool HasAudio() MOZ_OVERRIDE;
|
||||
bool HasVideo() MOZ_OVERRIDE;
|
||||
|
||||
// Returns references to the audio/video queues of sub-readers. Called on
|
||||
// decode, state machine and audio threads.
|
||||
MediaQueue<AudioData>& AudioQueue() MOZ_OVERRIDE;
|
||||
MediaQueue<VideoData>& VideoQueue() MOZ_OVERRIDE;
|
||||
|
||||
// Called from MediaDecoderStateMachine on the main thread.
|
||||
nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
|
||||
|
||||
// Used by |MediaMemoryReporter|.
|
||||
int64_t VideoQueueMemoryInUse() MOZ_OVERRIDE;
|
||||
int64_t AudioQueueMemoryInUse() MOZ_OVERRIDE;
|
||||
|
||||
// Called on the decode thread, at the start of the decode loop, before
|
||||
// |DecodeVideoFrame|. Carries out video reader switch if previously
|
||||
// requested, and tells sub-readers to |PrepareToDecode|.
|
||||
void PrepareToDecode() MOZ_OVERRIDE;
|
||||
|
||||
// Called on the decode thread.
|
||||
bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
bool DecodeAudioData() MOZ_OVERRIDE;
|
||||
|
||||
// Converts seek time to byte offset. Called on the decode thread only.
|
||||
nsresult Seek(int64_t aTime,
|
||||
int64_t aStartTime,
|
||||
int64_t aEndTime,
|
||||
int64_t aCurrentTime) MOZ_OVERRIDE;
|
||||
|
||||
// Called by state machine on multiple threads.
|
||||
nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE;
|
||||
|
||||
// Called on the state machine or decode threads.
|
||||
VideoData* FindStartTime(int64_t& aOutStartTime) MOZ_OVERRIDE;
|
||||
|
||||
// Prepares for an upcoming switch of video readers. Called by
|
||||
// |DASHDecoder| when it has switched download streams. Sets the index of
|
||||
// the reader to switch TO and the index of the subsegment to switch AT
|
||||
// (start offset). (Note: Subsegment boundaries are switch access points for
|
||||
// DASH-WebM). Called on the main thread. Must be in the decode monitor.
|
||||
void RequestVideoReaderSwitch(uint32_t aFromReaderIdx,
|
||||
uint32_t aToReaderIdx,
|
||||
uint32_t aSubsegmentIdx);
|
||||
|
||||
// Returns a pointer to the reader which should be used for the specified
|
||||
// subsegment. Called on the decode thread only.
|
||||
DASHRepReader* GetReaderForSubsegment(uint32_t aSubsegmentIdx);
|
||||
|
||||
private:
|
||||
// Switches video subreaders if a stream-switch flag has been set, and the
|
||||
// current reader has read up to the switching subsegment (start offset).
|
||||
// Called on the decode thread only.
|
||||
void PossiblySwitchVideoReaders();
|
||||
|
||||
// Monitor and booleans used to wait for metadata bytes to be downloaded, and
|
||||
// skip reading metadata if |DASHDecoder|'s shutdown is in progress.
|
||||
ReentrantMonitor mReadMetadataMonitor;
|
||||
bool mReadyToReadMetadata;
|
||||
bool mDecoderIsShuttingDown;
|
||||
|
||||
// Wrapper class protecting accesses to sub-readers. Asserts that the
|
||||
// decoder monitor has been entered for write access on all threads and read
|
||||
// access on all threads that are not the decode thread. Read access on the
|
||||
// decode thread does not need to be protected.
|
||||
class MonitoredSubReader
|
||||
{
|
||||
public:
|
||||
// Main constructor takes a pointer to the owning |DASHReader| to verify
|
||||
// correct entry into the decoder's |ReentrantMonitor|.
|
||||
MonitoredSubReader(DASHReader* aReader) :
|
||||
mReader(aReader),
|
||||
mSubReader(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DASHReader::MonitoredSubReader);
|
||||
NS_ASSERTION(mReader, "Reader is null!");
|
||||
}
|
||||
// Note: |mSubReader|'s refcount will be decremented in this destructor.
|
||||
~MonitoredSubReader()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DASHReader::MonitoredSubReader);
|
||||
}
|
||||
|
||||
// Override '=' to always assert thread is "in monitor" for writes/changes
|
||||
// to |mSubReader|.
|
||||
MonitoredSubReader& operator=(DASHRepReader* rhs)
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
mSubReader = rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Override '*' to assert threads other than the decode thread are "in
|
||||
// monitor" for ptr reads.
|
||||
operator DASHRepReader*() const
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
if (!mReader->GetDecoder()->OnDecodeThread()) {
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
}
|
||||
return mSubReader;
|
||||
}
|
||||
|
||||
// Override '->' to assert threads other than the decode thread are "in
|
||||
// monitor" for |mSubReader| function calls.
|
||||
DASHRepReader* operator->() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
// Pointer to |DASHReader| object which owns this |MonitoredSubReader|.
|
||||
DASHReader* mReader;
|
||||
// Ref ptr to the sub reader.
|
||||
nsRefPtr<DASHRepReader> mSubReader;
|
||||
};
|
||||
|
||||
// Wrapped ref ptrs to current sub-readers of individual media
|
||||
// |Representation|s. Decoder monitor must be entered for write access on all
|
||||
// threads and read access on all threads that are not the decode thread.
|
||||
// Read access on the decode thread does not need to be protected.
|
||||
// Note: |MonitoredSubReader| class will assert correct monitor use.
|
||||
MonitoredSubReader mAudioReader;
|
||||
MonitoredSubReader mVideoReader;
|
||||
|
||||
// Wrapper class protecting accesses to sub-reader list. Asserts that the
|
||||
// decoder monitor has been entered for write access on all threads and read
|
||||
// access on all threads that are not the decode thread. Read access on the
|
||||
// decode thread does not need to be protected.
|
||||
// Note: Elems accessed via operator[] are not protected with monitor
|
||||
// assertion checks once obtained.
|
||||
class MonitoredSubReaderList
|
||||
{
|
||||
public:
|
||||
// Main constructor takes a pointer to the owning |DASHReader| to verify
|
||||
// correct entry into the decoder's |ReentrantMonitor|.
|
||||
MonitoredSubReaderList(DASHReader* aReader) :
|
||||
mReader(aReader)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DASHReader::MonitoredSubReaderList);
|
||||
NS_ASSERTION(mReader, "Reader is null!");
|
||||
}
|
||||
// Note: Elements in |mSubReaderList| will have their refcounts decremented
|
||||
// in this destructor.
|
||||
~MonitoredSubReaderList()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DASHReader::MonitoredSubReaderList);
|
||||
}
|
||||
|
||||
// Returns Length of |mSubReaderList| array. Will assert threads other than
|
||||
// the decode thread are "in monitor".
|
||||
uint32_t Length() const
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
if (!mReader->GetDecoder()->OnDecodeThread()) {
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
}
|
||||
return mSubReaderList.Length();
|
||||
}
|
||||
|
||||
// Returns true if |mSubReaderList| is empty. Will assert that threads
|
||||
// other than the decode thread are "in monitor".
|
||||
bool IsEmpty() const
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
if (!mReader->GetDecoder()->OnDecodeThread()) {
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
}
|
||||
return mSubReaderList.IsEmpty();
|
||||
}
|
||||
// Override '[]' to assert threads other than the decode thread are "in
|
||||
// monitor" for accessing individual elems. Note: elems returned do not
|
||||
// have monitor assertions builtin like |MonitoredSubReader| objects.
|
||||
nsRefPtr<DASHRepReader>& operator[](uint32_t i)
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
if (!mReader->GetDecoder()->OnDecodeThread()) {
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
}
|
||||
return mSubReaderList[i];
|
||||
}
|
||||
|
||||
// Appends a reader to the end of |mSubReaderList|. Will always assert that
|
||||
// the thread is "in monitor".
|
||||
void
|
||||
AppendElement(DASHRepReader* aReader)
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
mSubReaderList.AppendElement(aReader);
|
||||
}
|
||||
private:
|
||||
// Pointer to |DASHReader| object which owns this |MonitoredSubReader|.
|
||||
DASHReader* mReader;
|
||||
// Ref ptrs to the sub readers.
|
||||
nsTArray<nsRefPtr<DASHRepReader> > mSubReaderList;
|
||||
};
|
||||
|
||||
// Ref ptrs to all sub-readers of individual media |Representation|s.
|
||||
// Decoder monitor must be entered for write access on all threads and read
|
||||
// access on all threads that are not the decode thread. Read acces on the
|
||||
// decode thread does not need to be protected.
|
||||
MonitoredSubReaderList mAudioReaders;
|
||||
MonitoredSubReaderList mVideoReaders;
|
||||
|
||||
// When true, indicates that we should switch reader. Must be in the monitor
|
||||
// for write access and read access off the decode thread.
|
||||
bool mSwitchVideoReaders;
|
||||
|
||||
// Indicates the subsegment index at which the reader should switch. Must be
|
||||
// in the monitor for write access and read access off the decode thread.
|
||||
nsTArray<uint32_t> mSwitchToVideoSubsegmentIndexes;
|
||||
|
||||
// Counts the number of switches that have taken place. Must be in the
|
||||
// monitor for write access and read access off the decode thread.
|
||||
int32_t mSwitchCount;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,517 +0,0 @@
|
||||
/* -*- 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.*/
|
||||
|
||||
#include "prlog.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "SegmentBase.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "DASHReader.h"
|
||||
#include "MediaResource.h"
|
||||
#include "DASHRepDecoder.h"
|
||||
#include "WebMReader.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define LOG(msg, ...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [DASHRepDecoder] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [DASHRepDecoder] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
MediaDecoderStateMachine*
|
||||
DASHRepDecoder::CreateStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
// Do not create; just return current state machine.
|
||||
return mDecoderStateMachine;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHRepDecoder::SetStateMachine(MediaDecoderStateMachine* aSM)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mDecoderStateMachine = aSM;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetResource(MediaResource* aResource)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mResource = aResource;
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetMPDRepresentation(Representation const * aRep)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mMPDRepresentation = aRep;
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetReader(WebMReader* aReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mReader = aReader;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHRepDecoder::Load(nsIStreamListener** aListener,
|
||||
MediaDecoder* aCloneDonor)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE(mMPDRepresentation, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Get init range and index range from MPD.
|
||||
SegmentBase const * segmentBase = mMPDRepresentation->GetSegmentBase();
|
||||
NS_ENSURE_TRUE(segmentBase, NS_ERROR_NULL_POINTER);
|
||||
|
||||
// Get and set init range.
|
||||
segmentBase->GetInitRange(&mInitByteRange.mStart, &mInitByteRange.mEnd);
|
||||
NS_ENSURE_TRUE(!mInitByteRange.IsNull(), NS_ERROR_NOT_INITIALIZED);
|
||||
mReader->SetInitByteRange(mInitByteRange);
|
||||
|
||||
// Get and set index range.
|
||||
segmentBase->GetIndexRange(&mIndexByteRange.mStart, &mIndexByteRange.mEnd);
|
||||
NS_ENSURE_TRUE(!mIndexByteRange.IsNull(), NS_ERROR_NOT_INITIALIZED);
|
||||
mReader->SetIndexByteRange(mIndexByteRange);
|
||||
|
||||
// Determine byte range to Open.
|
||||
// For small deltas between init and index ranges, we need to bundle the byte
|
||||
// range requests together in order to deal with |MediaCache|'s control of
|
||||
// seeking (see |MediaCache|::|Update|). |MediaCache| will not initiate a
|
||||
// |ChannelMediaResource|::|CacheClientSeek| for the INDEX byte range if the
|
||||
// delta between it and the INIT byte ranges is less than
|
||||
// |SEEK_VS_READ_THRESHOLD|. To get around this, request all metadata bytes
|
||||
// now so |MediaCache| can assume the bytes are en route.
|
||||
int64_t delta = std::max(mIndexByteRange.mStart, mInitByteRange.mStart)
|
||||
- std::min(mIndexByteRange.mEnd, mInitByteRange.mEnd);
|
||||
MediaByteRange byteRange;
|
||||
if (delta <= SEEK_VS_READ_THRESHOLD) {
|
||||
byteRange.mStart = std::min(mIndexByteRange.mStart, mInitByteRange.mStart);
|
||||
byteRange.mEnd = std::max(mIndexByteRange.mEnd, mInitByteRange.mEnd);
|
||||
// Loading everything in one chunk .
|
||||
mMetadataChunkCount = 1;
|
||||
} else {
|
||||
byteRange = mInitByteRange;
|
||||
// Loading in two chunks: init and index.
|
||||
mMetadataChunkCount = 2;
|
||||
}
|
||||
mCurrentByteRange = byteRange;
|
||||
return mResource->OpenByteRange(nullptr, byteRange);
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NotifyDownloadEnded(nsresult aStatus)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
if (!mMainDecoder) {
|
||||
if (!mShuttingDown) {
|
||||
LOG("Error! Main Decoder is null before shutdown: mMainDecoder [%p] ",
|
||||
mMainDecoder.get());
|
||||
DecodeError();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
// Decrement counter as metadata chunks are downloaded.
|
||||
// Note: Reader gets next chunk download via |ChannelMediaResource|:|Seek|.
|
||||
if (mMetadataChunkCount > 0) {
|
||||
LOG("Metadata chunk [%d] downloaded: range requested [%lld - %lld] "
|
||||
"subsegmentIdx [%d]",
|
||||
mMetadataChunkCount,
|
||||
mCurrentByteRange.mStart, mCurrentByteRange.mEnd, mSubsegmentIdx);
|
||||
mMetadataChunkCount--;
|
||||
} else {
|
||||
LOG("Byte range downloaded: status [%x] range requested [%lld - %lld] "
|
||||
"subsegmentIdx [%d]",
|
||||
aStatus, mCurrentByteRange.mStart, mCurrentByteRange.mEnd,
|
||||
mSubsegmentIdx);
|
||||
if ((uint32_t)mSubsegmentIdx == mByteRanges.Length()-1) {
|
||||
mResource->NotifyLastByteRange();
|
||||
}
|
||||
// Notify main decoder that a DATA byte range is downloaded.
|
||||
mMainDecoder->NotifyDownloadEnded(this, aStatus, mSubsegmentIdx);
|
||||
}
|
||||
} else if (aStatus == NS_BINDING_ABORTED) {
|
||||
LOG("Media download has been cancelled by the user: aStatus [%x].",
|
||||
aStatus);
|
||||
if (mMainDecoder) {
|
||||
mMainDecoder->LoadAborted();
|
||||
}
|
||||
return;
|
||||
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
|
||||
LOG("Network error trying to download MPD: aStatus [%x].", aStatus);
|
||||
NetworkError();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::OnReadMetadataCompleted()
|
||||
{
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// If shutting down, just return silently.
|
||||
if (mShuttingDown) {
|
||||
LOG1("Shutting down! Ignoring OnReadMetadataCompleted().");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG1("Metadata has been read.");
|
||||
|
||||
// Metadata loaded and read for this stream; ok to populate byte ranges.
|
||||
nsresult rv = PopulateByteRanges();
|
||||
if (NS_FAILED(rv) || mByteRanges.IsEmpty()) {
|
||||
LOG("Error populating byte ranges [%x]", rv);
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
mMainDecoder->OnReadMetadataCompleted(this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHRepDecoder::PopulateByteRanges()
|
||||
{
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// Should not be called during shutdown.
|
||||
NS_ENSURE_FALSE(mShuttingDown, NS_ERROR_UNEXPECTED);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
if (!mByteRanges.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ENSURE_TRUE(mReader, NS_ERROR_NULL_POINTER);
|
||||
LOG1("Populating byte range array.");
|
||||
return mReader->GetSubsegmentByteRanges(mByteRanges);
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::LoadNextByteRange()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ASSERTION(mResource, "Error: resource is reported as null!");
|
||||
|
||||
// Return silently if shutting down.
|
||||
if (mShuttingDown) {
|
||||
LOG1("Shutting down! Ignoring LoadNextByteRange().");
|
||||
return;
|
||||
}
|
||||
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
NS_ASSERTION(mMainDecoder, "Error: main decoder is null!");
|
||||
NS_ASSERTION(mMainDecoder->IsDecoderAllowedToDownloadData(this),
|
||||
"Should not be called on non-active decoders!");
|
||||
|
||||
// Cannot have empty byte ranges.
|
||||
if (mByteRanges.IsEmpty()) {
|
||||
LOG1("Error getting list of subsegment byte ranges.");
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get byte range for subsegment.
|
||||
int32_t subsegmentIdx = mMainDecoder->GetSubsegmentIndex(this);
|
||||
NS_ASSERTION(0 <= subsegmentIdx,
|
||||
"Subsegment index should be >= 0 for active decoders");
|
||||
if (subsegmentIdx >= 0 && (uint32_t)subsegmentIdx < mByteRanges.Length()) {
|
||||
mCurrentByteRange = mByteRanges[subsegmentIdx];
|
||||
mSubsegmentIdx = subsegmentIdx;
|
||||
} else {
|
||||
mCurrentByteRange.Clear();
|
||||
mSubsegmentIdx = -1;
|
||||
LOG("End of subsegments: index [%d] out of range.", subsegmentIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Request a seek for the first reader. Required so that the reader is
|
||||
// primed to start here, and will block subsequent subsegment seeks unless
|
||||
// the subsegment has been read.
|
||||
if (subsegmentIdx == 0) {
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mReader->RequestSeekToSubsegment(0);
|
||||
}
|
||||
|
||||
// Query resource for cached ranges; only download if it's not there.
|
||||
if (IsSubsegmentCached(mSubsegmentIdx)) {
|
||||
LOG("Subsegment [%d] bytes [%lld] to [%lld] already cached. No need to "
|
||||
"download.", mSubsegmentIdx,
|
||||
mCurrentByteRange.mStart, mCurrentByteRange.mEnd);
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &DASHRepDecoder::DoNotifyDownloadEnded);
|
||||
nsresult rv = NS_DispatchToMainThread(event);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error notifying subsegment [%d] cached: rv[0x%x].",
|
||||
mSubsegmentIdx, rv);
|
||||
NetworkError();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Open byte range corresponding to subsegment.
|
||||
nsresult rv = mResource->OpenByteRange(nullptr, mCurrentByteRange);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error opening byte range [%lld - %lld]: subsegmentIdx [%d] rv [%x].",
|
||||
mCurrentByteRange.mStart, mCurrentByteRange.mEnd, mSubsegmentIdx, rv);
|
||||
NetworkError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DASHRepDecoder::IsSubsegmentCached(int32_t aSubsegmentIdx)
|
||||
{
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
MediaByteRange byteRange = mByteRanges[aSubsegmentIdx];
|
||||
int64_t start = mResource->GetNextCachedData(byteRange.mStart);
|
||||
int64_t end = mResource->GetCachedDataEnd(byteRange.mStart);
|
||||
return (start == byteRange.mStart &&
|
||||
end >= byteRange.mEnd);
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::DoNotifyDownloadEnded()
|
||||
{
|
||||
NotifyDownloadEnded(NS_OK);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHRepDecoder::GetByteRangeForSeek(int64_t const aOffset,
|
||||
MediaByteRange& aByteRange)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
// Only check data ranges if they're available and if this decoder is active,
|
||||
// i.e. inactive rep decoders should only load metadata.
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
|
||||
for (uint32_t i = 0; i < mByteRanges.Length(); i++) {
|
||||
NS_ENSURE_FALSE(mByteRanges[i].IsNull(), NS_ERROR_NOT_INITIALIZED);
|
||||
// Check if |aOffset| lies within the current data range.
|
||||
if (mByteRanges[i].mStart <= aOffset && aOffset <= mByteRanges[i].mEnd) {
|
||||
if (mMainDecoder->IsDecoderAllowedToDownloadSubsegment(this, i)) {
|
||||
mCurrentByteRange = aByteRange = mByteRanges[i];
|
||||
mSubsegmentIdx = i;
|
||||
// XXX Hack: should be setting subsegment outside this function, but
|
||||
// need to review seeking for multiple switches anyhow.
|
||||
mMainDecoder->SetSubsegmentIndex(this, i);
|
||||
LOG("Getting DATA range [%d] for seek offset [%lld]: "
|
||||
"bytes [%lld] to [%lld]",
|
||||
i, aOffset, aByteRange.mStart, aByteRange.mEnd);
|
||||
return NS_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Don't allow metadata downloads once they're loaded and byte ranges have
|
||||
// been populated.
|
||||
bool canDownloadMetadata = mByteRanges.IsEmpty();
|
||||
if (canDownloadMetadata) {
|
||||
// Check metadata ranges; init range.
|
||||
if (mInitByteRange.mStart <= aOffset && aOffset <= mInitByteRange.mEnd) {
|
||||
mCurrentByteRange = aByteRange = mInitByteRange;
|
||||
mSubsegmentIdx = 0;
|
||||
LOG("Getting INIT range for seek offset [%lld]: bytes [%lld] to "
|
||||
"[%lld]", aOffset, aByteRange.mStart, aByteRange.mEnd);
|
||||
return NS_OK;
|
||||
}
|
||||
// ... index range.
|
||||
if (mIndexByteRange.mStart <= aOffset && aOffset <= mIndexByteRange.mEnd) {
|
||||
mCurrentByteRange = aByteRange = mIndexByteRange;
|
||||
mSubsegmentIdx = 0;
|
||||
LOG("Getting INDEXES range for seek offset [%lld]: bytes [%lld] to "
|
||||
"[%lld]", aOffset, aByteRange.mStart, aByteRange.mEnd);
|
||||
return NS_OK;
|
||||
}
|
||||
} else {
|
||||
LOG1("Metadata should be read; inhibiting further metadata downloads.");
|
||||
}
|
||||
|
||||
// If no byte range is found by this stage, clear the parameter and return.
|
||||
aByteRange.Clear();
|
||||
if (mByteRanges.IsEmpty() || !canDownloadMetadata) {
|
||||
// Assume mByteRanges will be populated after metadata is read.
|
||||
LOG("Data ranges not populated [%s]; metadata download restricted [%s]: "
|
||||
"offset[%lld].",
|
||||
(mByteRanges.IsEmpty() ? "yes" : "no"),
|
||||
(canDownloadMetadata ? "no" : "yes"), aOffset);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
} else {
|
||||
// Cannot seek to an unknown offset.
|
||||
// XXX Revisit this for dynamic MPD profiles if MPD is regularly updated.
|
||||
LOG("Error! Offset [%lld] is in an unknown range!", aOffset);
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::PrepareForSwitch()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
// Ensure that the media cache writes any data held in its partial block.
|
||||
mResource->FlushCache();
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NetworkError()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->NetworkError(); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetDuration(double aDuration)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->SetDuration(aDuration); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetInfinite(bool aInfinite)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->SetInfinite(aInfinite); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetMediaSeekable(bool aMediaSeekable)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
|
||||
"Should be on main thread or decode thread.");
|
||||
if (mMainDecoder) { mMainDecoder->SetMediaSeekable(aMediaSeekable); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::Progress(bool aTimer)
|
||||
{
|
||||
if (mMainDecoder) { mMainDecoder->Progress(aTimer); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
LOG("Data bytes [%lld - %lld] arrived via buffer [%p].",
|
||||
aOffset, aOffset+aLength, aBuffer);
|
||||
// Notify reader directly, since call to |MediaDecoderStateMachine|::
|
||||
// |NotifyDataArrived| will go to |DASHReader|::|NotifyDataArrived|, which
|
||||
// has no way to forward the notification to the correct sub-reader.
|
||||
if (mReader) {
|
||||
mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
}
|
||||
// Forward to main decoder which will notify state machine.
|
||||
if (mMainDecoder) {
|
||||
mMainDecoder->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NotifyBytesDownloaded()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->NotifyBytesDownloaded(); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NotifySuspendedStatusChanged()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->NotifySuspendedStatusChanged(); }
|
||||
}
|
||||
|
||||
bool
|
||||
DASHRepDecoder::OnStateMachineThread() const
|
||||
{
|
||||
return (mMainDecoder ? mMainDecoder->OnStateMachineThread() : false);
|
||||
}
|
||||
|
||||
bool
|
||||
DASHRepDecoder::OnDecodeThread() const
|
||||
{
|
||||
return (mMainDecoder ? mMainDecoder->OnDecodeThread() : false);
|
||||
}
|
||||
|
||||
ReentrantMonitor&
|
||||
DASHRepDecoder::GetReentrantMonitor()
|
||||
{
|
||||
NS_ASSERTION(mMainDecoder, "Can't get monitor if main decoder is null!");
|
||||
if (mMainDecoder) {
|
||||
return mMainDecoder->GetReentrantMonitor();
|
||||
} else {
|
||||
// XXX If mMainDecoder is gone, most likely we're past shutdown and
|
||||
// a waiting function has been wakened. Just return this decoder's own
|
||||
// monitor and let the function complete.
|
||||
return MediaDecoder::GetReentrantMonitor();
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::layers::ImageContainer*
|
||||
DASHRepDecoder::GetImageContainer()
|
||||
{
|
||||
return (mMainDecoder ? mMainDecoder->GetImageContainer() : nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::DecodeError()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
MediaDecoder::DecodeError();
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &MediaDecoder::DecodeError);
|
||||
nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error dispatching DecodeError event to main thread: rv[%x]", rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::ReleaseStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
||||
|
||||
// Since state machine owns mReader, remove reference to it.
|
||||
mReader = nullptr;
|
||||
|
||||
MediaDecoder::ReleaseStateMachine();
|
||||
}
|
||||
|
||||
void DASHRepDecoder::StopProgressUpdates()
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(mMainDecoder);
|
||||
MediaDecoder::StopProgressUpdates();
|
||||
}
|
||||
|
||||
void DASHRepDecoder::StartProgressUpdates()
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(mMainDecoder);
|
||||
MediaDecoder::StartProgressUpdates();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -1,237 +0,0 @@
|
||||
/* -*- 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(DASHRepDecoder_h_)
|
||||
#define DASHRepDecoder_h_
|
||||
|
||||
#include "Representation.h"
|
||||
#include "DASHDecoder.h"
|
||||
#include "WebMDecoder.h"
|
||||
#include "WebMReader.h"
|
||||
#include "MediaDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class ImageContainer;
|
||||
}
|
||||
|
||||
class DASHDecoder;
|
||||
class DASHRepReader;
|
||||
|
||||
class DASHRepDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
typedef mozilla::net::Representation Representation;
|
||||
typedef mozilla::net::SegmentBase SegmentBase;
|
||||
typedef mozilla::layers::ImageContainer ImageContainer;
|
||||
|
||||
// Constructor takes a ptr to the main decoder.
|
||||
DASHRepDecoder(DASHDecoder* aMainDecoder) :
|
||||
mMainDecoder(aMainDecoder),
|
||||
mMPDRepresentation(nullptr),
|
||||
mMetadataChunkCount(0),
|
||||
mCurrentByteRange(),
|
||||
mSubsegmentIdx(-1),
|
||||
mReader(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DASHRepDecoder);
|
||||
}
|
||||
|
||||
~DASHRepDecoder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DASHRepDecoder);
|
||||
}
|
||||
|
||||
// Clone not supported; just return nullptr.
|
||||
virtual MediaDecoder* Clone() { return nullptr; }
|
||||
|
||||
// Called by the main decoder at creation time; points to the main state
|
||||
// machine managed by the main decoder. Called on the main thread only.
|
||||
nsresult SetStateMachine(MediaDecoderStateMachine* aSM);
|
||||
|
||||
private:
|
||||
// Overridden to return the ptr set by SetStateMachine. Called on the main
|
||||
// thread only.
|
||||
MediaDecoderStateMachine* CreateStateMachine();
|
||||
|
||||
public:
|
||||
// Called by DASHDecoder at creation time; points to the media resource
|
||||
// for this decoder's |Representation|. Called on the main thread only.
|
||||
void SetResource(MediaResource* aResource);
|
||||
|
||||
// Sets the |Representation| object for this decoder. Called on the main
|
||||
// thread.
|
||||
void SetMPDRepresentation(Representation const * aRep);
|
||||
|
||||
// Called from DASHDecoder on main thread; Starts media stream download.
|
||||
virtual nsresult Load(nsIStreamListener** aListener = nullptr,
|
||||
MediaDecoder* aCloneDonor = nullptr) MOZ_OVERRIDE;
|
||||
|
||||
// Loads the next byte range (or first one on first call). Called on the main
|
||||
// thread only.
|
||||
void LoadNextByteRange();
|
||||
|
||||
// Returns true if the subsegment is already in the media cache.
|
||||
bool IsSubsegmentCached(int32_t aSubsegmentIdx);
|
||||
|
||||
// Calls from DASHRepDecoder. Called on the main thread only.
|
||||
void SetReader(WebMReader* aReader);
|
||||
|
||||
// Called if the media file encounters a network error. Call on the main
|
||||
// thread only.
|
||||
void NetworkError();
|
||||
|
||||
// Called from reader during ReadMetadata. This should be ignored here, and
|
||||
// instead, duration should be set following MPD parsing.
|
||||
void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE { };
|
||||
|
||||
// Set the duration of the media resource in units of seconds.
|
||||
// This is called via a channel listener if it can pick up the duration
|
||||
// from a content header. Must be called from the main thread only.
|
||||
virtual void SetDuration(double aDuration);
|
||||
|
||||
// Set media stream as infinite. Called on the main thread only.
|
||||
void SetInfinite(bool aInfinite);
|
||||
|
||||
// Sets media stream as seekable. Called on main thread only.
|
||||
void SetMediaSeekable(bool aSeekable);
|
||||
|
||||
// Fire progress events if needed according to the time and byte
|
||||
// constraints outlined in the specification. aTimer is true
|
||||
// if the method is called as a result of the progress timer rather
|
||||
// than the result of downloaded data.
|
||||
void Progress(bool aTimer);
|
||||
|
||||
// Called as data arrives on the stream and is read into the cache. Called
|
||||
// on the main thread only.
|
||||
void NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset);
|
||||
|
||||
// Called by MediaResource when some data has been received.
|
||||
// Call on the main thread only.
|
||||
void NotifyBytesDownloaded();
|
||||
|
||||
// Notify that a byte range request has been completed by the media resource.
|
||||
// Called on the main thread only.
|
||||
void NotifyDownloadEnded(nsresult aStatus);
|
||||
|
||||
// Called asynchronously by |LoadNextByteRange| if the data is already in the
|
||||
// media cache. This will call NotifyDownloadEnded on the main thread with
|
||||
// |aStatus| of NS_OK.
|
||||
void DoNotifyDownloadEnded();
|
||||
|
||||
// Called by MediaResource when the "cache suspended" status changes.
|
||||
// If MediaResource::IsSuspendedByCache returns true, then the decoder
|
||||
// should stop buffering or otherwise waiting for download progress and
|
||||
// start consuming data, if possible, because the cache is full.
|
||||
void NotifySuspendedStatusChanged();
|
||||
|
||||
// Increments the parsed and decoded frame counters by the passed in counts.
|
||||
// Can be called on any thread.
|
||||
void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_OVERRIDE {
|
||||
if (mMainDecoder) {mMainDecoder->NotifyDecodedFrames(aParsed, aDecoded); }
|
||||
}
|
||||
|
||||
// Gets a byte range containing the byte offset. Call on main thread only.
|
||||
nsresult GetByteRangeForSeek(int64_t const aOffset,
|
||||
MediaByteRange& aByteRange);
|
||||
|
||||
// Gets the number of data byte ranges (not inc. metadata).
|
||||
uint32_t GetNumDataByteRanges() {
|
||||
return mByteRanges.Length();
|
||||
}
|
||||
|
||||
// Notify that a switch is about to happen. Called on the main thread.
|
||||
void PrepareForSwitch();
|
||||
|
||||
// Returns true if the current thread is the state machine thread.
|
||||
bool OnStateMachineThread() const MOZ_OVERRIDE;
|
||||
|
||||
// Returns true if the current thread is the decode thread.
|
||||
bool OnDecodeThread() const MOZ_OVERRIDE;
|
||||
|
||||
// Returns main decoder's monitor for synchronised access.
|
||||
ReentrantMonitor& GetReentrantMonitor() MOZ_OVERRIDE;
|
||||
|
||||
// Called on the decode thread from WebMReader.
|
||||
ImageContainer* GetImageContainer() MOZ_OVERRIDE;
|
||||
|
||||
// Called when Metadata has been read; notifies that index data is read.
|
||||
// Called on the decode thread only.
|
||||
void OnReadMetadataCompleted() MOZ_OVERRIDE;
|
||||
|
||||
// Stop updating the bytes downloaded for progress notifications. Called
|
||||
// when seeking to prevent wild changes to the progress notification.
|
||||
// Must be called with the decoder monitor held.
|
||||
void StopProgressUpdates() MOZ_OVERRIDE;
|
||||
|
||||
// Allow updating the bytes downloaded for progress notifications. Must
|
||||
// be called with the decoder monitor held.
|
||||
void StartProgressUpdates() MOZ_OVERRIDE;
|
||||
|
||||
// Overridden to cleanup ref to |DASHDecoder|. Called on main thread only.
|
||||
void Shutdown() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
// Remove ref to state machine before |MediaDecoder|::|Shutdown|, since
|
||||
// |DASHDecoder| is responsible for its shutdown.
|
||||
mDecoderStateMachine = nullptr;
|
||||
// Call parent class shutdown.
|
||||
MediaDecoder::Shutdown();
|
||||
NS_ENSURE_TRUE_VOID(mShuttingDown);
|
||||
// Cleanup ref to main decoder.
|
||||
mMainDecoder = nullptr;
|
||||
}
|
||||
|
||||
// Drop reference to state machine and mReader (owned by state machine).
|
||||
// Only called during shutdown dance.
|
||||
void ReleaseStateMachine();
|
||||
|
||||
// Notifies the element that decoding has failed.
|
||||
void DecodeError();
|
||||
|
||||
private:
|
||||
// Populates |mByteRanges| by calling |GetIndexByteRanges| from |mReader|.
|
||||
// Called on the main thread only.
|
||||
nsresult PopulateByteRanges();
|
||||
|
||||
// The main decoder.
|
||||
nsRefPtr<DASHDecoder> mMainDecoder;
|
||||
// This decoder's MPD |Representation| object.
|
||||
Representation const * mMPDRepresentation;
|
||||
|
||||
// Countdown var for loading metadata byte ranges.
|
||||
uint16_t mMetadataChunkCount;
|
||||
|
||||
// All the byte ranges for this |Representation|.
|
||||
nsTArray<MediaByteRange> mByteRanges;
|
||||
|
||||
// Byte range for the init and index bytes.
|
||||
MediaByteRange mInitByteRange;
|
||||
MediaByteRange mIndexByteRange;
|
||||
|
||||
// The current byte range being requested.
|
||||
MediaByteRange mCurrentByteRange;
|
||||
// Index of the current byte range. Initialized to -1.
|
||||
int32_t mSubsegmentIdx;
|
||||
|
||||
// Ptr to the reader object for this |Representation|. Owned by state
|
||||
// machine.
|
||||
DASHRepReader* mReader;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif //DASHRepDecoder_h_
|
@ -1,68 +0,0 @@
|
||||
/* -*- 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 comments on DASH object interaction
|
||||
*/
|
||||
|
||||
#if !defined(DASHRepReader_h_)
|
||||
#define DASHRepReader_h_
|
||||
|
||||
#include "VideoUtils.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "DASHReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DASHReader;
|
||||
|
||||
class DASHRepReader : public MediaDecoderReader
|
||||
{
|
||||
public:
|
||||
DASHRepReader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaDecoderReader(aDecoder) { }
|
||||
virtual ~DASHRepReader() { }
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DASHRepReader)
|
||||
|
||||
virtual void SetMainReader(DASHReader *aMainReader) = 0;
|
||||
|
||||
// Sets range for initialization bytes; used by DASH.
|
||||
virtual void SetInitByteRange(MediaByteRange &aByteRange) = 0;
|
||||
|
||||
// Sets range for index frame bytes; used by DASH.
|
||||
virtual void SetIndexByteRange(MediaByteRange &aByteRange) = 0;
|
||||
|
||||
// Returns the index of the subsegment which contains the seek time (usecs).
|
||||
virtual int64_t GetSubsegmentForSeekTime(int64_t aSeekToTime) = 0;
|
||||
|
||||
// Returns list of ranges for index frame start/end offsets. Used by DASH.
|
||||
virtual nsresult GetSubsegmentByteRanges(nsTArray<MediaByteRange>& aByteRanges) = 0;
|
||||
|
||||
// Returns true if the reader has reached a DASH switch access point.
|
||||
virtual bool HasReachedSubsegment(uint32_t aSubsegmentIndex) = 0;
|
||||
|
||||
// Requests a seek to the start of a particular DASH subsegment.
|
||||
virtual void RequestSeekToSubsegment(uint32_t aIdx) = 0;
|
||||
|
||||
// Reader should stop reading at the start of the specified subsegment, and
|
||||
// should prepare for the next reader to add data to the video queue.
|
||||
// Should be implemented by a sub-reader, e.g. |nsDASHWebMReader|.
|
||||
virtual void RequestSwitchAtSubsegment(int32_t aCluster,
|
||||
MediaDecoderReader* aNextReader) = 0;
|
||||
|
||||
// Returns true if data at the end of the final subsegment has been cached.
|
||||
virtual bool IsDataCachedAtEndOfSubsegments() = 0;
|
||||
};
|
||||
|
||||
}// namespace mozilla
|
||||
|
||||
#endif /*DASHRepReader*/
|
@ -1,19 +0,0 @@
|
||||
# -*- Mode: makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- #
|
||||
# vim: set ts=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/.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Steve Workman <sworkman@mozilla.com>
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES := \
|
||||
-I$(topsrcdir)/netwerk/dash/mpd \
|
||||
-I$(srcdir)/../webm \
|
||||
-I$(srcdir)/../../base/src \
|
||||
-I$(srcdir)/../../html/content/src \
|
||||
$(MOZ_LIBVPX_INCLUDES) \
|
||||
$(NULL)
|
@ -1,25 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
MODULE = 'content'
|
||||
|
||||
EXPORTS += [
|
||||
'DASHDecoder.h',
|
||||
'DASHReader.h',
|
||||
'DASHRepDecoder.h',
|
||||
'DASHRepReader.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'DASHDecoder.cpp',
|
||||
'DASHReader.cpp',
|
||||
'DASHRepDecoder.cpp',
|
||||
]
|
||||
|
||||
LIBRARY_NAME = 'gkcondash_s'
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
@ -26,9 +26,6 @@ if CONFIG['MOZ_WEBM']:
|
||||
if CONFIG['MOZ_GSTREAMER']:
|
||||
PARALLEL_DIRS += ['gstreamer']
|
||||
|
||||
if CONFIG['MOZ_DASH']:
|
||||
PARALLEL_DIRS += ['dash']
|
||||
|
||||
if CONFIG['MOZ_DIRECTSHOW']:
|
||||
PARALLEL_DIRS += ['directshow']
|
||||
|
||||
|
@ -199,12 +199,6 @@ SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/content/media/mediasource/$(LIB_PREFIX)gkconmediasource_s.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_DASH
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/content/media/dash/$(LIB_PREFIX)gkcondash_s.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_WEBSPEECH
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/content/media/webspeech/recognition/$(LIB_PREFIX)gkconwebspeechrecognition_s.$(LIB_SUFFIX) \
|
||||
|
@ -63,13 +63,6 @@ NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult);
|
||||
#define CONTENTDLF_WEBM_CATEGORIES
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
#define CONTENTDLF_DASH_CATEGORIES \
|
||||
{ "Gecko-Content-Viewers", APPLICATION_DASH, "@mozilla.org/content/document-loader-factory;1" },
|
||||
#else
|
||||
#define CONTENTDLF_DASH_CATEGORIES
|
||||
#endif
|
||||
|
||||
#define CONTENTDLF_CATEGORIES \
|
||||
{ "Gecko-Content-Viewers", TEXT_HTML, "@mozilla.org/content/document-loader-factory;1" }, \
|
||||
{ "Gecko-Content-Viewers", TEXT_PLAIN, "@mozilla.org/content/document-loader-factory;1" }, \
|
||||
@ -91,8 +84,7 @@ NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult);
|
||||
{ "Gecko-Content-Viewers", VIEWSOURCE_CONTENT_TYPE, "@mozilla.org/content/document-loader-factory;1" }, \
|
||||
{ "Gecko-Content-Viewers", IMAGE_SVG_XML, "@mozilla.org/content/document-loader-factory;1" }, \
|
||||
{ "Gecko-Content-Viewers", APPLICATION_MATHML_XML, "@mozilla.org/content/document-loader-factory;1" }, \
|
||||
CONTENTDLF_WEBM_CATEGORIES \
|
||||
CONTENTDLF_DASH_CATEGORIES
|
||||
CONTENTDLF_WEBM_CATEGORIES
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -215,9 +215,6 @@ pref("media.wave.enabled", true);
|
||||
#ifdef MOZ_WEBM
|
||||
pref("media.webm.enabled", true);
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
pref("media.dash.enabled", false);
|
||||
#endif
|
||||
#ifdef MOZ_GSTREAMER
|
||||
pref("media.gstreamer.enabled", true);
|
||||
#endif
|
||||
|
@ -55,11 +55,6 @@ ifeq (android,$(MOZ_WIDGET_TOOLKIT))
|
||||
../system/android/$(LIB_PREFIX)neckosystem_s.$(LIB_SUFFIX)
|
||||
endif
|
||||
|
||||
ifdef MOZ_DASH
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
../dash/mpd/$(LIB_PREFIX)nkdashmpd_s.$(LIB_SUFFIX)
|
||||
endif
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/../base/src \
|
||||
-I$(srcdir)/../dns \
|
||||
|
@ -1,7 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
PARALLEL_DIRS += ['mpd']
|
@ -1,122 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* AdaptationSet.cpp
|
||||
*****************************************************************************
|
||||
* Copyright(C) 2010 - 2012 Klagenfurt University
|
||||
*
|
||||
* Created on: Jan 27, 2012
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* |AdaptationSet|
|
||||
*
|
||||
* Describes a type of media in a |Period| of time in the media presentation,
|
||||
* e.g. an audio or video stream. Direct child of |Period|, which contains 1+
|
||||
* available pieces of media, available during that time period.
|
||||
* |AdaptationSet| itself contains one or more |Representations| which describe
|
||||
* different versions of the media, most commonly different bitrate encodings.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#include "AdaptationSet.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
int32_t
|
||||
AdaptationSet::GetWidth() const
|
||||
{
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
void
|
||||
AdaptationSet::SetWidth(int32_t const aWidth)
|
||||
{
|
||||
mWidth = aWidth;
|
||||
}
|
||||
|
||||
int32_t
|
||||
AdaptationSet::GetHeight() const
|
||||
{
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
void
|
||||
AdaptationSet::SetHeight(int32_t const aHeight)
|
||||
{
|
||||
mHeight = aHeight;
|
||||
}
|
||||
|
||||
void
|
||||
AdaptationSet::GetMIMEType(nsAString& aMIMEType) const
|
||||
{
|
||||
aMIMEType = mMIMEType;
|
||||
}
|
||||
|
||||
void
|
||||
AdaptationSet::SetMIMEType(nsAString const &aMIMEType)
|
||||
{
|
||||
NS_ENSURE_FALSE_VOID(aMIMEType.IsEmpty());
|
||||
mMIMEType = aMIMEType;
|
||||
}
|
||||
|
||||
Representation const *
|
||||
AdaptationSet::GetRepresentation(uint32_t aIndex) const
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex < mRepresentations.Length(), nullptr);
|
||||
return mRepresentations[aIndex];
|
||||
}
|
||||
|
||||
void
|
||||
AdaptationSet::AddRepresentation(Representation* aRep)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(aRep);
|
||||
// Only add if it's not already in the array.
|
||||
if (!mRepresentations.Contains(aRep)) {
|
||||
mRepresentations.InsertElementSorted(aRep, CompareRepresentationBitrates());
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t
|
||||
AdaptationSet::GetNumRepresentations() const
|
||||
{
|
||||
return mRepresentations.Length();
|
||||
}
|
||||
|
||||
void
|
||||
AdaptationSet::EnableBitstreamSwitching(bool aEnable)
|
||||
{
|
||||
mIsBitstreamSwitching = aEnable;
|
||||
}
|
||||
|
||||
bool
|
||||
AdaptationSet::IsBitstreamSwitchingEnabled() const
|
||||
{
|
||||
return mIsBitstreamSwitching;
|
||||
}
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
@ -1,107 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* AdaptationSet.h
|
||||
*****************************************************************************
|
||||
* Copyright(C) 2010 - 2012 Klagenfurt University
|
||||
*
|
||||
* Created on: Jan 27, 2012
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* |AdaptationSet|
|
||||
*
|
||||
* Describes a type of media in a |Period| of time in the media presentation,
|
||||
* e.g. an audio or video stream. Direct child of |Period|, which contains 1+
|
||||
* available pieces of media, available during that time period.
|
||||
* |AdaptationSet| itself contains one or more |Representations| which describe
|
||||
* different versions of the media, most commonly different bitrate encodings.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#ifndef ADAPTATIONSET_H_
|
||||
#define ADAPTATIONSET_H_
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "Representation.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class AdaptationSet
|
||||
{
|
||||
public:
|
||||
AdaptationSet() :
|
||||
mWidth(0),
|
||||
mHeight(0),
|
||||
mIsBitstreamSwitching(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AdaptationSet);
|
||||
}
|
||||
virtual ~AdaptationSet() {
|
||||
MOZ_COUNT_DTOR(AdaptationSet);
|
||||
}
|
||||
|
||||
// Setters and getters for @width, @height and @mimetype.
|
||||
int32_t GetWidth() const;
|
||||
void SetWidth(int32_t const aWidth);
|
||||
int32_t GetHeight() const;
|
||||
void SetHeight(int32_t const aHeight);
|
||||
void GetMIMEType(nsAString& aMIMEType) const;
|
||||
void SetMIMEType(nsAString const &aMIMEType);
|
||||
|
||||
// Gets a list of media |Representation| objects for this |AdaptationSet|.
|
||||
Representation const * GetRepresentation(uint32_t) const;
|
||||
|
||||
// Adds a media |Representation| to this |AdaptationSet|. Takes ownership to
|
||||
// manage deletion.
|
||||
void AddRepresentation(Representation* aRep);
|
||||
|
||||
// Returns the number of media |Representations|.
|
||||
uint16_t GetNumRepresentations() const;
|
||||
|
||||
// En/Dis-ables switching between media |Representation|s.
|
||||
void EnableBitstreamSwitching(bool const aEnable);
|
||||
bool IsBitstreamSwitchingEnabled() const;
|
||||
|
||||
private:
|
||||
// Array of media |Representations| to switch between.
|
||||
// Ordered list, ascending in order of bitrates.
|
||||
nsTArray<nsAutoPtr<Representation> > mRepresentations;
|
||||
|
||||
// @width, height and @mimetype of this media stream.
|
||||
int32_t mWidth;
|
||||
int32_t mHeight;
|
||||
nsString mMIMEType;
|
||||
|
||||
// If true, switching between media |Representation|s is allowed.
|
||||
bool mIsBitstreamSwitching;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ADAPTATIONSET_H_ */
|
@ -1,54 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* IMPDManager.cpp
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Apr 20, 2011
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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/.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "nsIDOMElement.h"
|
||||
#include "IMPDManager.h"
|
||||
#include "nsDASHWebMODManager.h"
|
||||
#include "nsDASHWebMODParser.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
/* static */
|
||||
IMPDManager*
|
||||
IMPDManager::Create(DASHMPDProfile aProfile, nsIDOMElement* aRoot)
|
||||
{
|
||||
switch(aProfile)
|
||||
{
|
||||
case WebMOnDemand:
|
||||
return CreateWebMOnDemandManager(aRoot);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
IMPDManager*
|
||||
IMPDManager::CreateWebMOnDemandManager(nsIDOMElement* aRoot)
|
||||
{
|
||||
// Parse DOM elements into MPD objects.
|
||||
nsDASHWebMODParser parser(aRoot);
|
||||
|
||||
return new nsDASHWebMODManager(parser.Parse());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
@ -1,133 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts =2 et sw =2 tw =80: */
|
||||
/*
|
||||
* IMPDManager.h
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
* Media Presentation Description (MPD) Manager.
|
||||
*
|
||||
* Interface to MPD classes, populated with data from MPD XML manifest.
|
||||
* Employs adaptation algorithm to determine best representation of media to
|
||||
* download.
|
||||
* Adaptation algorithm is separate and passed into manager.
|
||||
* Interface aims to be an abstracted API for all DASH MPD Profiles
|
||||
* (e.g. DASH WebM On Demand Profile).
|
||||
*/
|
||||
|
||||
#ifndef IMPDMANAGER_H_
|
||||
#define IMPDMANAGER_H_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIDOMElement.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// DASH MPD Profile Type
|
||||
enum DASHMPDProfile
|
||||
{
|
||||
WebMOnDemand,
|
||||
NotValid
|
||||
// Extend this for other types.
|
||||
};
|
||||
|
||||
class Period;
|
||||
class Representation;
|
||||
class Segment;
|
||||
|
||||
class IMPDManager
|
||||
{
|
||||
public:
|
||||
// Describes the media type (audio/video) of the |AdaptationSet|.
|
||||
enum AdaptationSetType {
|
||||
DASH_ASTYPE_INVALID = 0,
|
||||
DASH_VIDEO_STREAM,
|
||||
DASH_AUDIO_STREAM,
|
||||
DAHS_AUDIO_VIDEO_STREAM
|
||||
};
|
||||
IMPDManager()
|
||||
{
|
||||
MOZ_COUNT_CTOR(IMPDManager);
|
||||
}
|
||||
|
||||
virtual ~IMPDManager()
|
||||
{
|
||||
MOZ_COUNT_DTOR(IMPDManager);
|
||||
}
|
||||
|
||||
// Used to get the first |Period| in the presentation.
|
||||
virtual Period const * GetFirstPeriod() const = 0;
|
||||
|
||||
// Gets the total number of |AdaptationSet|s in the first |Period|.
|
||||
// Usually, this should be 2 for audio and video.
|
||||
// XXX Future versions may require a |Period| index.
|
||||
// XXX Future versions may have multiple tracks for audio.
|
||||
virtual uint32_t GetNumAdaptationSets() const = 0;
|
||||
|
||||
// Returns the media type for the given |AdaptationSet|, audio/video.
|
||||
virtual AdaptationSetType
|
||||
GetAdaptationSetType(uint32_t const aAdaptSetIdx) const = 0;
|
||||
|
||||
// Gets the number of media |Representation|s for the given |AdaptationSet|.
|
||||
// e.g how many bitrate encodings are there of the audio stream?
|
||||
virtual uint32_t
|
||||
GetNumRepresentations(uint32_t const aAdaptSetIdx) const = 0;
|
||||
|
||||
// Gets the specified |Representation| from the specified |AdaptationSet|,
|
||||
// e.g. get metadata about the 64Kbps encoding of the video stream.
|
||||
virtual Representation const *
|
||||
GetRepresentation(uint32_t const aAdaptSetIdx,
|
||||
uint32_t const aRepIdx) const = 0;
|
||||
|
||||
// Gets the URL of the first media |Segment| for the specific media
|
||||
// |Representation|, e.g. the url of the first 64Kbps video segment.
|
||||
virtual nsresult GetFirstSegmentUrl(uint32_t const aAdaptSetIdx,
|
||||
uint32_t const aRepIdx,
|
||||
nsAString &aUrl) const = 0;
|
||||
|
||||
// Returns the start time of the presentation in seconds.
|
||||
virtual double GetStartTime() const = 0;
|
||||
|
||||
// Returns the duration of the presentation in seconds.
|
||||
virtual double GetDuration() const = 0;
|
||||
|
||||
// Gets index of the |Representation| with next highest bitrate to the
|
||||
// estimated bandwidth passed in. Returns true if there is at least one
|
||||
// |Representation| with a bitrate lower than |aBandwidth|; otherwise returns
|
||||
// false. Depends on |mRepresentations| being an ordered list.
|
||||
virtual bool GetBestRepForBandwidth(uint32_t aAdaptSetIdx,
|
||||
uint64_t aBandwidth,
|
||||
uint32_t &aRepIdx) const = 0;
|
||||
public:
|
||||
// Factory method.
|
||||
static IMPDManager* Create(DASHMPDProfile Profile, nsIDOMElement* aRoot);
|
||||
|
||||
private:
|
||||
// Used by factory method.
|
||||
static IMPDManager* CreateWebMOnDemandManager(nsIDOMElement* aRoot);
|
||||
};
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
||||
|
||||
#endif /* IMPDMANAGER_H_ */
|
@ -1,53 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* IMPDParser.h
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
* Media Presentation Description (MPD) Parser.
|
||||
*
|
||||
* Interface to MPD parser. Derived classes parse MPD XML manifest and populate
|
||||
* MPD classes for use by MPD Manager.
|
||||
* Interface aims to be an abstracted API for all DASH MPD Profiles
|
||||
* (e.g. DASH WebM On Demand Profile).
|
||||
*/
|
||||
|
||||
#ifndef IMPDPARSER_H_
|
||||
#define IMPDPARSER_H_
|
||||
|
||||
#include "MPD.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class IMPDParser
|
||||
{
|
||||
public:
|
||||
// Parses XML file to create and populate MPD classes.
|
||||
// Called by MPD Manager.
|
||||
virtual MPD* Parse() = 0;
|
||||
};
|
||||
|
||||
}// namespace net
|
||||
}// namespace mozilla
|
||||
|
||||
#endif /* IMPDPARSER_H_ */
|
@ -1,91 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* MPD.cpp
|
||||
*****************************************************************************
|
||||
* Copyright(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* |MPD| - Media Presentation Description
|
||||
*
|
||||
* Describes the media presentation. Top of the hierarchy in an MPD file.
|
||||
* Contains one or a series of contiguous |Period|s, which contain 1+ available
|
||||
* pieces of media, available during that time period, e.g. audio in various
|
||||
* languages, a video component.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "MPD.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
void
|
||||
MPD::AddPeriod(Period* aPeriod)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(aPeriod);
|
||||
// Only add |Period| if it's not in the array already.
|
||||
if (!mPeriods.Contains(aPeriod)) {
|
||||
mPeriods.AppendElement(aPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
Period const *
|
||||
MPD::GetPeriod(uint32_t aIndex) const
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex < mPeriods.Length(), nullptr);
|
||||
return mPeriods[aIndex];
|
||||
}
|
||||
|
||||
uint32_t const
|
||||
MPD::GetNumPeriods() const
|
||||
{
|
||||
return mPeriods.Length();
|
||||
}
|
||||
|
||||
void
|
||||
MPD::AddBaseUrl(nsAString const& aUrl)
|
||||
{
|
||||
NS_ENSURE_FALSE_VOID(aUrl.IsEmpty());
|
||||
// Only add |BaseUrl| string if it's not in the array already.
|
||||
if (!mBaseUrls.Contains(aUrl)) {
|
||||
mBaseUrls.AppendElement(aUrl);
|
||||
}
|
||||
}
|
||||
|
||||
nsAString const&
|
||||
MPD::GetBaseUrl(uint32_t aIndex) const
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex < mBaseUrls.Length(), NS_LITERAL_STRING(""));
|
||||
return mBaseUrls[aIndex];
|
||||
}
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
@ -1,90 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* MPD.h
|
||||
*****************************************************************************
|
||||
* Copyright(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* |MPD| - Media Presentation Description
|
||||
*
|
||||
* Describes the media presentation. Top of the hierarchy in an MPD file.
|
||||
* Contains one or a series of contiguous |Period|s, which contain 1+ available
|
||||
* pieces of media, available during that time period, e.g. audio in various
|
||||
* languages, a video component.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#ifndef MPD_H_
|
||||
#define MPD_H_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "Period.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class MPD
|
||||
{
|
||||
public:
|
||||
MPD()
|
||||
{
|
||||
MOZ_COUNT_CTOR(MPD);
|
||||
}
|
||||
virtual ~MPD() {
|
||||
MOZ_COUNT_DTOR(MPD);
|
||||
}
|
||||
|
||||
// At least one media content |Period|s per Media Presentation. The |MPD|
|
||||
// contains 1+ available pieces of media, available during that time period
|
||||
// e.g. audio in various languages, a video component.
|
||||
// |MPD| takes ownership of |Period| in |AddPeriod| and will manage deletion.
|
||||
void AddPeriod(Period* aPeriod);
|
||||
Period const * GetPeriod(uint32_t aIndex) const;
|
||||
uint32_t const GetNumPeriods() const;
|
||||
|
||||
// Adds/Gets an absolute/relative |BaseURL| for the whole document.
|
||||
// Note: A relative |BaseURL| for the whole document will use the URL for the
|
||||
// MPD file itself as base.
|
||||
void AddBaseUrl(nsAString const& aUrl);
|
||||
nsAString const& GetBaseUrl(uint32_t aIndex) const;
|
||||
bool HasBaseUrls() { return !mBaseUrls.IsEmpty(); }
|
||||
|
||||
private:
|
||||
// List of media content |Period|s in this media presentation.
|
||||
nsTArray<nsAutoPtr<Period> > mPeriods;
|
||||
|
||||
// List of |BaseURL|s which can be used to access the media.
|
||||
nsTArray<nsString> mBaseUrls;
|
||||
};
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
||||
|
||||
#endif /* MPD_H_ */
|
@ -1,15 +0,0 @@
|
||||
# -*- Mode: makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- #
|
||||
# vim: set ts=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/.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Steve Workman <sworkman@mozilla.com>
|
||||
|
||||
LOCAL_INCLUDES := \
|
||||
-I$(topsrcdir)/content/base/src \
|
||||
-I$(topsrcdir)/content/html/content/public \
|
||||
-I$(topsrcdir)/content/html/content/src \
|
||||
$(NULL)
|
@ -1,98 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* Period.cpp
|
||||
*****************************************************************************
|
||||
* Copyright(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* |Period|
|
||||
*
|
||||
* Describes a period of time in the media presentation. Direct child of |MPD|.
|
||||
* Alone, or one of a series of contiguous |Period|s, which contain 1+ available
|
||||
* pieces of media, available during that time period, e.g. audio in various
|
||||
* languages, a video component.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "Period.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
AdaptationSet const *
|
||||
Period::GetAdaptationSet(uint32_t aIndex) const
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex < mAdaptationSets.Length(), nullptr);
|
||||
return mAdaptationSets[aIndex];
|
||||
}
|
||||
|
||||
void
|
||||
Period::AddAdaptationSet(AdaptationSet* aAdaptationSet)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(aAdaptationSet);
|
||||
// Only add |AdaptationSet| ptr if it's not in the array already.
|
||||
if (!mAdaptationSets.Contains(aAdaptationSet)) {
|
||||
mAdaptationSets.AppendElement(aAdaptationSet);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t const
|
||||
Period::GetNumAdaptationSets() const
|
||||
{
|
||||
return mAdaptationSets.Length();
|
||||
}
|
||||
|
||||
double const
|
||||
Period::GetStart() const
|
||||
{
|
||||
return mStart;
|
||||
}
|
||||
|
||||
double const
|
||||
Period::GetDuration() const
|
||||
{
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
void
|
||||
Period::SetStart(double const aStart)
|
||||
{
|
||||
mStart = aStart;
|
||||
}
|
||||
|
||||
void
|
||||
Period::SetDuration(double const aDuration)
|
||||
{
|
||||
mDuration = aDuration;
|
||||
}
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
@ -1,96 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* Period.h
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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/.
|
||||
*****************************************************************************/
|
||||
#ifndef PERIOD_H_
|
||||
#define PERIOD_H_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "AdaptationSet.h"
|
||||
#include "Representation.h"
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* |Period|
|
||||
*
|
||||
* Describes a period of time in the media presentation. Direct child of |MPD|.
|
||||
* Alone, or one of a series of contiguous |Period|s, which contain 1+ available
|
||||
* pieces of media, available during that time period, e.g. audio in various
|
||||
* languages, a video component.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class Period
|
||||
{
|
||||
public:
|
||||
Period()
|
||||
{
|
||||
MOZ_COUNT_CTOR(Period);
|
||||
}
|
||||
virtual ~Period() {
|
||||
MOZ_COUNT_DTOR(Period);
|
||||
}
|
||||
|
||||
// Gets/Adds |AdaptationSet|s of media for this media content |Period|.
|
||||
AdaptationSet const * GetAdaptationSet(uint32_t aIndex) const;
|
||||
// |Period| takes ownership of |AdaptationSet| here and will manage deletion.
|
||||
void AddAdaptationSet(AdaptationSet* aAdaptationSet);
|
||||
|
||||
// Returns the num. of |AdaptationSet|s in this media content |Period|.
|
||||
uint16_t const GetNumAdaptationSets() const;
|
||||
|
||||
// Gets/Sets the start time of this media content |Period| in seconds.
|
||||
double const GetStart() const;
|
||||
void SetStart(double const aStart);
|
||||
|
||||
// Gets/Sets the duration of this media content |Period| in seconds.
|
||||
double const GetDuration() const;
|
||||
void SetDuration(double const aDuration);
|
||||
|
||||
private:
|
||||
// List of |AdaptationSet|s of media in this |Period|.
|
||||
nsTArray<nsAutoPtr<AdaptationSet> > mAdaptationSets;
|
||||
|
||||
// Start time in seconds for this |Period|.
|
||||
double mStart;
|
||||
|
||||
// Duration in seconds for this |Period|.
|
||||
double mDuration;
|
||||
};
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
||||
|
||||
|
||||
#endif /* PERIOD_H_ */
|
@ -1,120 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* Representation.cpp
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* |Representation|
|
||||
*
|
||||
* Describes a particular version of a piece of media described in an
|
||||
* |AdaptationSet|, a common example being a particular bitrate encoding for an
|
||||
* audio or video stream. Direct child of |AdaptationSet|, which contains 1+
|
||||
* available |Representation|s of the media.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "Representation.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
int64_t const
|
||||
Representation::GetBitrate() const
|
||||
{
|
||||
return mBitrate;
|
||||
}
|
||||
|
||||
void
|
||||
Representation::SetBitrate(int64_t aBitrate)
|
||||
{
|
||||
mBitrate = aBitrate;
|
||||
}
|
||||
|
||||
void
|
||||
Representation::SetWidth(int32_t const aWidth)
|
||||
{
|
||||
mWidth = aWidth;
|
||||
}
|
||||
|
||||
int32_t const
|
||||
Representation::GetWidth() const
|
||||
{
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
void
|
||||
Representation::SetHeight(int32_t aHeight)
|
||||
{
|
||||
mHeight = aHeight;
|
||||
}
|
||||
|
||||
int32_t const
|
||||
Representation::GetHeight() const
|
||||
{
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
void
|
||||
Representation::AddBaseUrl(nsAString const& aUrl)
|
||||
{
|
||||
NS_ENSURE_FALSE_VOID(aUrl.IsEmpty());
|
||||
// Only add if it's not already in the array.
|
||||
if (!mBaseUrls.Contains(aUrl)) {
|
||||
mBaseUrls.AppendElement(aUrl);
|
||||
}
|
||||
}
|
||||
|
||||
nsAString const &
|
||||
Representation::GetBaseUrl(uint32_t aIndex) const
|
||||
{
|
||||
NS_ENSURE_TRUE(aIndex < mBaseUrls.Length(), NS_LITERAL_STRING(""));
|
||||
return mBaseUrls[aIndex];
|
||||
}
|
||||
|
||||
SegmentBase const*
|
||||
Representation::GetSegmentBase() const
|
||||
{
|
||||
return mSegmentBase;
|
||||
}
|
||||
|
||||
void
|
||||
Representation::SetSegmentBase(SegmentBase* aBase)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(aBase);
|
||||
// Don't reassign if the ptrs or contents are equal.
|
||||
if (mSegmentBase != aBase
|
||||
|| (mSegmentBase && (*mSegmentBase != *aBase))) {
|
||||
mSegmentBase = aBase;
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
@ -1,137 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* Representation.h
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* |Representation|
|
||||
*
|
||||
* Describes a particular version of a piece of media described in an
|
||||
* |AdaptationSet|, a common example being a particular bitrate encoding for an
|
||||
* audio or video stream. Direct child of |AdaptationSet|, which contains 1+
|
||||
* available |Representation|s of the media.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#ifndef REPRESENTATION_H_
|
||||
#define REPRESENTATION_H_
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "SegmentBase.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class Representation
|
||||
{
|
||||
public:
|
||||
Representation() :
|
||||
mBitrate(0),
|
||||
mWidth(0),
|
||||
mHeight(0),
|
||||
mSegmentBase(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(Representation);
|
||||
}
|
||||
virtual ~Representation() {
|
||||
MOZ_COUNT_DTOR(Representation);
|
||||
}
|
||||
|
||||
bool operator<(const Representation &other) const {
|
||||
return this->mBitrate < other.mBitrate;
|
||||
}
|
||||
|
||||
// Gets/Sets @bitrate in kbps.
|
||||
int64_t const GetBitrate() const;
|
||||
void SetBitrate(int64_t const aBitrate);
|
||||
|
||||
// Gets/Sets @width and @height for the media if it's video.
|
||||
void SetWidth(int32_t const aWidth);
|
||||
int32_t const GetWidth() const;
|
||||
void SetHeight(int32_t const aHeight);
|
||||
int32_t const GetHeight() const;
|
||||
|
||||
// Gets/Adds a |BaseURL| for the media files.
|
||||
void AddBaseUrl(nsAString const& aUrl);
|
||||
nsAString const& GetBaseUrl(uint32_t aIndex) const;
|
||||
bool HasBaseUrls() const { return !mBaseUrls.IsEmpty(); }
|
||||
|
||||
// Gets/Sets a base |Segment| for the |Representation|.
|
||||
SegmentBase const* GetSegmentBase() const;
|
||||
// Takes ownership of |SegmentBase| to manage deletion.
|
||||
void SetSegmentBase(SegmentBase* aBase);
|
||||
|
||||
private:
|
||||
// Bitrate of the media in kbps.
|
||||
int64_t mBitrate;
|
||||
|
||||
// Width and height of the media if video.
|
||||
int32_t mWidth;
|
||||
int32_t mHeight;
|
||||
|
||||
// List of absolute/relative |BaseURL|s which may be used to access the media.
|
||||
nsTArray<nsString> mBaseUrls;
|
||||
|
||||
// The base |Segment| for the |Representation|.
|
||||
nsAutoPtr<SegmentBase> mSegmentBase;
|
||||
};
|
||||
|
||||
// Comparator allows comparing |Representation|s based on media stream bitrate.
|
||||
class CompareRepresentationBitrates
|
||||
{
|
||||
public:
|
||||
// Returns true if the elements are equals; false otherwise.
|
||||
// Note: |Representation| is stored as an array of |nsAutoPtr| in
|
||||
// |AdaptationSet|, but needs to be compared to regular pointers.
|
||||
// Hence the left hand side of the function being an
|
||||
// |nsAutoPtr| and the right being a regular pointer.
|
||||
bool Equals(const nsAutoPtr<Representation>& a,
|
||||
const Representation *b) const {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
// Returns true if (a < b); false otherwise.
|
||||
// Note: |Representation| is stored as an array of |nsAutoPtr| in
|
||||
// |AdaptationSet|, but needs to be compared to regular pointers.
|
||||
// Hence the left hand side of the function being an
|
||||
// |nsAutoPtr| and the right being a regular pointer.
|
||||
bool LessThan(const nsAutoPtr<Representation>& a,
|
||||
const Representation *b) const {
|
||||
return *a < *b;
|
||||
}
|
||||
};
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
||||
|
||||
|
||||
#endif /* REPRESENTATION_H_ */
|
@ -1,106 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* SegmentBase.cpp
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2012 Klagenfurt University
|
||||
*
|
||||
* Created on: Jan 27, 2012
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* |SegmentBase|
|
||||
*
|
||||
* Describes common initialization information for |Segment|s in a
|
||||
* |Representation|.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#include "nsString.h"
|
||||
#include "SegmentBase.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
void
|
||||
SegmentBase::GetIndexRange(int64_t* aStartBytes, int64_t* aEndBytes) const
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(aStartBytes);
|
||||
NS_ENSURE_TRUE_VOID(aEndBytes);
|
||||
*aStartBytes = mIndexRangeStart;
|
||||
*aEndBytes = mIndexRangeEnd;
|
||||
}
|
||||
|
||||
void
|
||||
SegmentBase::GetInitRange(int64_t* aStartBytes, int64_t* aEndBytes) const
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(aStartBytes);
|
||||
NS_ENSURE_TRUE_VOID(aEndBytes);
|
||||
*aStartBytes = mInitRangeStart;
|
||||
*aEndBytes = mInitRangeEnd;
|
||||
}
|
||||
|
||||
void
|
||||
SegmentBase::SetIndexRange(nsAString const &aRangeStr)
|
||||
{
|
||||
SetRange(aRangeStr, mIndexRangeStart, mIndexRangeEnd);
|
||||
}
|
||||
|
||||
void
|
||||
SegmentBase::SetInitRange(nsAString const &aRangeStr)
|
||||
{
|
||||
SetRange(aRangeStr, mInitRangeStart, mInitRangeEnd);
|
||||
}
|
||||
|
||||
void
|
||||
SegmentBase::SetRange(nsAString const &aRangeStr,
|
||||
int64_t &aStart,
|
||||
int64_t &aEnd)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(!aRangeStr.IsEmpty());
|
||||
|
||||
nsAString::const_iterator start, end, dashStart, dashEnd;
|
||||
|
||||
aRangeStr.BeginReading(start);
|
||||
aRangeStr.EndReading(end);
|
||||
dashStart = start;
|
||||
dashEnd = end;
|
||||
|
||||
if (FindInReadable(NS_LITERAL_STRING("-"), dashStart, dashEnd)) {
|
||||
nsAutoString temp(Substring(start, dashStart));
|
||||
nsresult rv;
|
||||
aStart = temp.ToInteger64(&rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
temp = Substring(dashEnd, end);
|
||||
aEnd = temp.ToInteger64(&rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
@ -1,100 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* SegmentBase.h
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2012 Klagenfurt University
|
||||
*
|
||||
* Created on: Jan 27, 2012
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* |SegmentBase|
|
||||
*
|
||||
* Describes common initialization information for |Segment|s in a
|
||||
* |Representation|.
|
||||
*
|
||||
* Common class used by all DASH Profiles.
|
||||
* Populated by implementation of MPD Parser.
|
||||
* Used as data source by implementation of MPD Manager.
|
||||
*
|
||||
* |MPD|
|
||||
* --> |Period|s of time.
|
||||
* --> |AdaptationSet|s for each type or group of media content.
|
||||
* --> |Representation|s of media, encoded with different bitrates.
|
||||
* --> |Segment|s of media, identified by URL (+optional byte
|
||||
* range.
|
||||
*/
|
||||
|
||||
#ifndef SEGMENTBASE_H_
|
||||
#define SEGMENTBASE_H_
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class SegmentBase
|
||||
{
|
||||
public:
|
||||
SegmentBase() :
|
||||
mInitRangeStart(0),
|
||||
mInitRangeEnd(0),
|
||||
mIndexRangeStart(0),
|
||||
mIndexRangeEnd(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SegmentBase);
|
||||
}
|
||||
virtual ~SegmentBase()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SegmentBase);
|
||||
}
|
||||
|
||||
bool operator==(SegmentBase const & other) const {
|
||||
return (mInitRangeStart == other.mInitRangeStart
|
||||
&& mInitRangeEnd == other.mInitRangeEnd
|
||||
&& mIndexRangeStart == other.mIndexRangeStart
|
||||
&& mIndexRangeEnd == other.mIndexRangeEnd);
|
||||
}
|
||||
bool operator!=(SegmentBase const & other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
// Get/Set the byte range for the initialization bytes.
|
||||
void GetInitRange(int64_t* aStartBytes, int64_t* aEndBytes) const;
|
||||
void SetInitRange(nsAString const &aRangeStr);
|
||||
|
||||
// Get/Set the byte range for the index bytes.
|
||||
void GetIndexRange(int64_t* aStartBytes, int64_t* aEndBytes) const;
|
||||
void SetIndexRange(nsAString const &aRangeStr);
|
||||
|
||||
private:
|
||||
// Parses the string to get a start and end value.
|
||||
void SetRange(nsAString const &aRangeStr, int64_t &aStart, int64_t &aEnd);
|
||||
|
||||
// Start and end values for the init byte range.
|
||||
int64_t mInitRangeStart;
|
||||
int64_t mInitRangeEnd;
|
||||
|
||||
// Start and end values for the index byte range.
|
||||
int64_t mIndexRangeStart;
|
||||
int64_t mIndexRangeEnd;
|
||||
};
|
||||
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
||||
|
||||
#endif /* SEGMENTBASE_H_ */
|
@ -1,24 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
MODULE = 'necko'
|
||||
|
||||
SOURCES += [
|
||||
'AdaptationSet.cpp',
|
||||
'IMPDManager.cpp',
|
||||
'MPD.cpp',
|
||||
'nsDASHMPDParser.cpp',
|
||||
'nsDASHWebMODManager.cpp',
|
||||
'nsDASHWebMODParser.cpp',
|
||||
'Period.cpp',
|
||||
'Representation.cpp',
|
||||
'SegmentBase.cpp',
|
||||
]
|
||||
|
||||
LIBRARY_NAME = 'nkdashmpd_s'
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
@ -1,219 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* nsDASHMPDParser.cpp
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "prlog.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIDOMAttr.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOMParser.h"
|
||||
#include "nsIDOMMozNamedAttrMap.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsString.h"
|
||||
#include "IMPDManager.h"
|
||||
#include "nsDASHMPDParser.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo* gDASHMPDParserLog = nullptr;
|
||||
#define LOG(msg, ...) PR_LOG(gDASHMPDParserLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHMPDParser] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) PR_LOG(gDASHMPDParserLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHMPDParser] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
nsDASHMPDParser::nsDASHMPDParser(char* aMPDData,
|
||||
uint32_t aDataLength,
|
||||
nsIPrincipal* aPrincipal,
|
||||
nsIURI* aURI) :
|
||||
mData(aMPDData),
|
||||
mDataLength(aDataLength),
|
||||
mPrincipal(aPrincipal),
|
||||
mURI(aURI)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDASHMPDParser);
|
||||
#if defined(PR_LOGGING)
|
||||
if(!gDASHMPDParserLog)
|
||||
gDASHMPDParserLog = PR_NewLogModule("nsDASHMPDParser");
|
||||
#endif
|
||||
}
|
||||
|
||||
nsDASHMPDParser::~nsDASHMPDParser()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsDASHMPDParser);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDASHMPDParser::Parse(IMPDManager** aMPDManager,
|
||||
DASHMPDProfile* aProfile)
|
||||
{
|
||||
NS_ENSURE_ARG(aMPDManager);
|
||||
NS_ENSURE_ARG(aProfile);
|
||||
NS_ENSURE_TRUE(mData, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString spec;
|
||||
nsresult rv = mURI->GetSpec(spec);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG1("Preparing to parse MPD: cannot get spec from URI");
|
||||
} else {
|
||||
LOG("Preparing to parse MPD: mURI:\"%s\"", spec.get());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get mDoc element from mData buffer using DOMParser.
|
||||
nsCOMPtr<nsIDOMParser> DOMParser;
|
||||
DOMParser = do_CreateInstance(NS_DOMPARSER_CONTRACTID);
|
||||
nsresult rv = DOMParser->Init(mPrincipal, mURI, nullptr, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> doc;
|
||||
rv = DOMParser->ParseFromBuffer(reinterpret_cast<uint8_t const *>(mData.get()),
|
||||
mDataLength,
|
||||
"application/xml",
|
||||
getter_AddRefs(doc));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if(!doc) {
|
||||
LOG1("ERROR! Document not parsed as XML!");
|
||||
return NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
// Use root node to create MPD manager.
|
||||
nsCOMPtr<nsIDOMElement> root;
|
||||
rv = doc->GetDocumentElement(getter_AddRefs(root));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
|
||||
#ifdef PR_LOGGING
|
||||
PrintDOMElements(root);
|
||||
#endif
|
||||
rv = GetProfile(root, *aProfile);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
*aMPDManager = IMPDManager::Create(*aProfile, root);
|
||||
NS_ENSURE_TRUE(*aMPDManager, NS_ERROR_NULL_POINTER);
|
||||
|
||||
// Get profile.
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHMPDParser::PrintDOMElement(nsIDOMElement* aElem, int32_t offset)
|
||||
{
|
||||
// Populate string ss and then print to LOG().
|
||||
nsAutoString ss;
|
||||
// Indent.
|
||||
for(int32_t i = 0; i < offset; i++)
|
||||
ss.Append(NS_LITERAL_STRING(" "));
|
||||
// Tag name.
|
||||
nsAutoString tagName;
|
||||
NS_ENSURE_SUCCESS_VOID(aElem->GetTagName(tagName));
|
||||
ss += NS_LITERAL_STRING("<");
|
||||
ss += tagName;
|
||||
|
||||
// Attributes.
|
||||
nsCOMPtr<nsIDOMMozNamedAttrMap> attributes;
|
||||
NS_ENSURE_SUCCESS_VOID(aElem->GetAttributes(getter_AddRefs(attributes)));
|
||||
|
||||
uint32_t count;
|
||||
NS_ENSURE_SUCCESS_VOID(attributes->GetLength(&count));
|
||||
|
||||
for(uint32_t i = 0; i < count; i++)
|
||||
{
|
||||
ss += NS_LITERAL_STRING(" ");
|
||||
nsCOMPtr<nsIDOMAttr> attr;
|
||||
NS_ENSURE_SUCCESS_VOID(attributes->Item(i, getter_AddRefs(attr)));
|
||||
|
||||
nsAutoString name;
|
||||
NS_ENSURE_SUCCESS_VOID(attr->GetName(name));
|
||||
ss += name;
|
||||
|
||||
nsAutoString value;
|
||||
NS_ENSURE_SUCCESS_VOID(attr->GetValue(value));
|
||||
if (!value.IsEmpty()) {
|
||||
ss += NS_LITERAL_STRING("=");
|
||||
ss += value;
|
||||
}
|
||||
}
|
||||
ss += NS_LITERAL_STRING(">");
|
||||
LOG("%s", NS_ConvertUTF16toUTF8(ss).get());
|
||||
|
||||
offset++;
|
||||
|
||||
// Print for each child.
|
||||
nsCOMPtr<nsIDOMElement> child;
|
||||
NS_ENSURE_SUCCESS_VOID(aElem->GetFirstElementChild(getter_AddRefs(child)));
|
||||
|
||||
while(child)
|
||||
{
|
||||
PrintDOMElement(child, offset);
|
||||
NS_ENSURE_SUCCESS_VOID(child->GetNextElementSibling(getter_AddRefs(child)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsDASHMPDParser::PrintDOMElements(nsIDOMElement* aRoot)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(aRoot);
|
||||
|
||||
DASHMPDProfile profile;
|
||||
NS_ENSURE_SUCCESS_VOID(GetProfile(aRoot, profile));
|
||||
LOG("Profile Is %d",(int32_t)profile);
|
||||
PrintDOMElement(aRoot, 0);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDASHMPDParser::GetProfile(nsIDOMElement* aRoot,
|
||||
DASHMPDProfile &aProfile)
|
||||
{
|
||||
NS_ENSURE_ARG(aRoot);
|
||||
|
||||
nsAutoString profileStr;
|
||||
nsresult rv = aRoot->GetAttribute(NS_LITERAL_STRING("profiles"), profileStr);
|
||||
LOG("profileStr: %s", NS_ConvertUTF16toUTF8(profileStr).get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (profileStr
|
||||
== NS_LITERAL_STRING("urn:webm:dash:profile:webm-on-demand:2012")) {
|
||||
aProfile = WebMOnDemand;
|
||||
} else {
|
||||
aProfile = NotValid;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
}// namespace net
|
||||
}// namespace mozilla
|
@ -1,78 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* nsDASHMPDParser.h
|
||||
*****************************************************************************
|
||||
* Copyright(C) 2010 - 2011 Klagenfurt University
|
||||
*
|
||||
* Created on: Aug 10, 2010
|
||||
* Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Contributors:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __DASHMPDPARSER_H__
|
||||
#define __DASHMPDPARSER_H__
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "IMPDManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class nsDASHMPDParser
|
||||
{
|
||||
public:
|
||||
// Constructor takes pointer to MPD byte stream and length, as well as the
|
||||
// current principal and URI from which the MPD download took place.
|
||||
// Ownership of |aMPDData| should be transferred to this object.
|
||||
nsDASHMPDParser(char* aMPDData,
|
||||
uint32_t aDataLength,
|
||||
nsIPrincipal* aPrincipal,
|
||||
nsIURI* aURI);
|
||||
|
||||
~nsDASHMPDParser();
|
||||
|
||||
// Parses the MPD byte stream passed in the constructor.
|
||||
// Returns a pointer to the MPDManager and the MPD profile type.
|
||||
nsresult Parse(IMPDManager** aMPDManager,
|
||||
DASHMPDProfile* aProfile);
|
||||
private:
|
||||
// Returns the MPD profile type given the DOM node for the root.
|
||||
nsresult GetProfile(nsIDOMElement* aRoot,
|
||||
DASHMPDProfile &profile);
|
||||
// Debug: Prints the DOM elements.
|
||||
void PrintDOMElements(nsIDOMElement* aRoot);
|
||||
// Debug: Prints a single DOM element.
|
||||
void PrintDOMElement(nsIDOMElement* aElem, int32_t aOffset);
|
||||
|
||||
// Pointer to the MPD byte stream.
|
||||
nsAutoPtr<char const> mData;
|
||||
// Length in bytes of the MPD stream.
|
||||
uint32_t mDataLength;
|
||||
// Principal of the document.
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
// URI of the MPD Document downloaded.
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
};
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
||||
|
||||
#endif /* __DASHMPDPARSER_H__ */
|
@ -1,236 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* nsDASHWebMODManager.cpp
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2012 Klagenfurt University
|
||||
*
|
||||
* Created on: May 1, 2012
|
||||
* Based on IsoffMainManager.cpp by:
|
||||
* Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Author:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
* Media Presentation Description (MPD) Manager for WebM On Demand Profile.
|
||||
*
|
||||
* Implements MPD Manager interface to use Adaptation Algorithm to determine
|
||||
* which stream to download from WebM On Demand-based MPD.
|
||||
*
|
||||
* Note: Adaptation algorithm is separate and passed into manager.
|
||||
*/
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "prlog.h"
|
||||
#include "SegmentBase.h"
|
||||
#include "nsDASHWebMODManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo* gnsDASHWebMODManagerLog = nullptr;
|
||||
#define LOG(msg, ...) \
|
||||
PR_LOG(gnsDASHWebMODManagerLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHWebMODManager] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) PR_LOG(gDASHMPDParserLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHWebMODManager] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
nsDASHWebMODManager::nsDASHWebMODManager(MPD* aMpd)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDASHWebMODManager);
|
||||
NS_ENSURE_TRUE_VOID(aMpd);
|
||||
mMpd = aMpd;
|
||||
#if defined(PR_LOGGING)
|
||||
if(!gnsDASHWebMODManagerLog)
|
||||
gnsDASHWebMODManagerLog = PR_NewLogModule("nsDASHWebMODManager");
|
||||
#endif
|
||||
LOG("Created nsDASHWebMODManager with mMpd(%p)", mMpd.get());
|
||||
}
|
||||
|
||||
nsDASHWebMODManager::~nsDASHWebMODManager()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsDASHWebMODManager);
|
||||
}
|
||||
|
||||
|
||||
Period const *
|
||||
nsDASHWebMODManager::GetFirstPeriod() const
|
||||
{
|
||||
NS_ENSURE_TRUE(mMpd, nullptr);
|
||||
NS_ENSURE_TRUE(0 < mMpd->GetNumPeriods(), nullptr);
|
||||
return mMpd->GetPeriod(0);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODManager::GetFirstSegmentUrl(uint32_t const aAdaptSetIdx,
|
||||
uint32_t const aRepIdx,
|
||||
nsAString &aUrl) const
|
||||
{
|
||||
NS_ENSURE_TRUE(mMpd, NS_ERROR_NULL_POINTER);
|
||||
// Append base URL for MPD.
|
||||
if (mMpd->HasBaseUrls()) {
|
||||
aUrl.Append(mMpd->GetBaseUrl(0));
|
||||
LOG("BaseUrl \"%s\"", NS_ConvertUTF16toUTF8(aUrl).get());
|
||||
}
|
||||
|
||||
// Append base URL for Representation.
|
||||
AdaptationSet const * adaptSet = GetAdaptationSet(aAdaptSetIdx);
|
||||
NS_ENSURE_TRUE(adaptSet, NS_ERROR_NULL_POINTER);
|
||||
|
||||
NS_ENSURE_TRUE(aRepIdx < adaptSet->GetNumRepresentations(),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
Representation const * rep = adaptSet->GetRepresentation(aRepIdx);
|
||||
NS_ENSURE_TRUE(rep, NS_ERROR_NULL_POINTER);
|
||||
|
||||
if (rep->HasBaseUrls()) {
|
||||
aUrl.Append(rep->GetBaseUrl(0));
|
||||
LOG("Appending \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(rep->GetBaseUrl(0)).get());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsDASHWebMODManager::GetNumAdaptationSets() const
|
||||
{
|
||||
Period const * current = GetFirstPeriod();
|
||||
return current ? current->GetNumAdaptationSets() : 0;
|
||||
}
|
||||
|
||||
IMPDManager::AdaptationSetType
|
||||
nsDASHWebMODManager::GetAdaptationSetType(uint32_t const aAdaptSetIdx) const
|
||||
{
|
||||
AdaptationSet const * adaptSet = GetAdaptationSet(aAdaptSetIdx);
|
||||
NS_ENSURE_TRUE(adaptSet, DASH_ASTYPE_INVALID);
|
||||
|
||||
nsAutoString mimeType;
|
||||
adaptSet->GetMIMEType(mimeType);
|
||||
NS_ENSURE_TRUE(!mimeType.IsEmpty(), DASH_ASTYPE_INVALID);
|
||||
|
||||
return GetAdaptationSetType(mimeType);
|
||||
}
|
||||
|
||||
IMPDManager::AdaptationSetType
|
||||
nsDASHWebMODManager::GetAdaptationSetType(nsAString const & aMimeType) const
|
||||
{
|
||||
NS_ENSURE_TRUE(!aMimeType.IsEmpty(), DASH_ASTYPE_INVALID);
|
||||
|
||||
if (aMimeType == NS_LITERAL_STRING(VIDEO_WEBM)) {
|
||||
return DASH_VIDEO_STREAM;
|
||||
} else if (aMimeType == NS_LITERAL_STRING(AUDIO_WEBM)) {
|
||||
return DASH_AUDIO_STREAM;
|
||||
} else {
|
||||
return DASH_ASTYPE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsDASHWebMODManager::GetNumRepresentations(uint32_t const aAdaptSetIdx) const
|
||||
{
|
||||
AdaptationSet const * adaptSet = GetAdaptationSet(aAdaptSetIdx);
|
||||
NS_ENSURE_TRUE(adaptSet, DASH_ASTYPE_INVALID);
|
||||
|
||||
return adaptSet->GetNumRepresentations();
|
||||
}
|
||||
|
||||
AdaptationSet const *
|
||||
nsDASHWebMODManager::GetAdaptationSet(uint32_t const aAdaptSetIdx) const
|
||||
{
|
||||
Period const * current = GetFirstPeriod();
|
||||
NS_ENSURE_TRUE(current, nullptr);
|
||||
|
||||
NS_ENSURE_TRUE(0 < current->GetNumAdaptationSets(), nullptr);
|
||||
NS_ENSURE_TRUE(aAdaptSetIdx < current->GetNumAdaptationSets(), nullptr);
|
||||
AdaptationSet const * adaptSet = current->GetAdaptationSet(aAdaptSetIdx);
|
||||
NS_ENSURE_TRUE(adaptSet, nullptr);
|
||||
return adaptSet;
|
||||
}
|
||||
|
||||
Representation const *
|
||||
nsDASHWebMODManager::GetRepresentation(uint32_t const aAdaptSetIdx,
|
||||
uint32_t const aRepIdx) const
|
||||
{
|
||||
AdaptationSet const * adaptSet = GetAdaptationSet(aAdaptSetIdx);
|
||||
NS_ENSURE_TRUE(adaptSet, nullptr);
|
||||
|
||||
NS_ENSURE_TRUE(aRepIdx < adaptSet->GetNumRepresentations(), nullptr);
|
||||
Representation const * rep = adaptSet->GetRepresentation(aRepIdx);
|
||||
NS_ENSURE_TRUE(rep, nullptr);
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
double
|
||||
nsDASHWebMODManager::GetStartTime() const
|
||||
{
|
||||
Period const * current = GetFirstPeriod();
|
||||
NS_ENSURE_TRUE(current, -1);
|
||||
|
||||
return current->GetStart();
|
||||
}
|
||||
|
||||
double
|
||||
nsDASHWebMODManager::GetDuration() const
|
||||
{
|
||||
Period const * current = GetFirstPeriod();
|
||||
NS_ENSURE_TRUE(current, -1);
|
||||
|
||||
return current->GetDuration();
|
||||
}
|
||||
|
||||
bool
|
||||
nsDASHWebMODManager::GetBestRepForBandwidth(uint32_t aAdaptSetIdx,
|
||||
uint64_t aBandwidth,
|
||||
uint32_t &aRepIdx) const
|
||||
{
|
||||
NS_ENSURE_TRUE(aAdaptSetIdx < GetNumAdaptationSets(), false);
|
||||
NS_ENSURE_TRUE(0 < GetNumRepresentations(aAdaptSetIdx), false);
|
||||
// Return false if there isn't enough bandwidth for even the lowest bitrate.
|
||||
// Let calling function decide what to do. Use 0.95 multiplier to deal with
|
||||
// 5% variance in bandwidth.
|
||||
// XXX Multiplier is a guess at present.
|
||||
if (aBandwidth*0.95 < GetRepresentation(aAdaptSetIdx, 0)->GetBitrate()) {
|
||||
aRepIdx = UINT32_MAX;
|
||||
return false;
|
||||
}
|
||||
// Iterate until the current |Representation|'s bitrate is higher than the
|
||||
// estimated available bandwidth. Use 0.95 multiplier to deal with 5%
|
||||
// variance in bandwidth.
|
||||
// XXX Multiplier is a guess at present.
|
||||
for (uint32_t i = 1; i < GetNumRepresentations(aAdaptSetIdx); i++) {
|
||||
NS_ENSURE_TRUE(GetRepresentation(aAdaptSetIdx, i), false);
|
||||
if (aBandwidth*0.95 < GetRepresentation(aAdaptSetIdx, i)->GetBitrate()) {
|
||||
// Pick the previous one, since this one's bitrate is too high.
|
||||
aRepIdx = i-1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// If we reach here, all of the |Representation|'s bitrates are lower than the
|
||||
// available bandwidth. Just pick the highest, i.e. last in the array.
|
||||
aRepIdx = GetNumRepresentations(aAdaptSetIdx)-1;
|
||||
return true;
|
||||
}
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
||||
|
@ -1,104 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* nsDASHWebMODManager.h
|
||||
*****************************************************************************
|
||||
*
|
||||
* Created on: May 1, 2012
|
||||
* Based on IsoffMainManager.h by:
|
||||
* Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Author:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
*
|
||||
* Media Presentation Description (MPD) Manager for WebM On Demand Profile.
|
||||
* ======================
|
||||
*
|
||||
* Implements MPD Manager interface to use Adaptation Algorithm to determine
|
||||
* which stream to download from WebM On Demand-based MPD.
|
||||
*
|
||||
* WebM On Demand describes a static, on demand media presentation using WebM
|
||||
* encoded media files.
|
||||
* Notes:
|
||||
* 1 time |Period| per media presentation |MPD|.
|
||||
* 1 audio stream (1 |Representation| for the audio |AdaptationSet|).
|
||||
* 1 or multiple video streams for stream switching:
|
||||
* (multiple |Representation|s for the video |AdaptationSet|).
|
||||
* 1 file for each encoding (1 |Segment| per |Representation|).
|
||||
* Stream switching allowed between clusters (clusters match DASH
|
||||
* subsegments).
|
||||
*
|
||||
* Note: Adaptation algorithm is separate and passed into manager.
|
||||
* XXX Adaptation not yet implemented.
|
||||
*/
|
||||
|
||||
#ifndef _NSDASHWEBMODMANAGER_H_
|
||||
#define _NSDASHWEBMODMANAGER_H_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsString.h"
|
||||
#include "MPD.h"
|
||||
#include "Period.h"
|
||||
#include "AdaptationSet.h"
|
||||
#include "Representation.h"
|
||||
#include "IMPDManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class nsDASHWebMODManager : public IMPDManager
|
||||
{
|
||||
public:
|
||||
nsDASHWebMODManager(MPD* mpd);
|
||||
~nsDASHWebMODManager();
|
||||
|
||||
// See IMPDManager.h for descriptions of following inherited functions.
|
||||
Period const * GetFirstPeriod() const;
|
||||
uint32_t GetNumAdaptationSets() const;
|
||||
AdaptationSetType
|
||||
GetAdaptationSetType(uint32_t const aAdaptSetIdx) const;
|
||||
uint32_t GetNumRepresentations(uint32_t const aAdaptSetIdx) const;
|
||||
Representation const * GetRepresentation(uint32_t const aAdaptSetIdx,
|
||||
uint32_t const aRepIdx) const;
|
||||
nsresult GetFirstSegmentUrl(uint32_t const aAdaptSetIdx,
|
||||
uint32_t const aRepIdx,
|
||||
nsAString &aUrl) const;
|
||||
double GetStartTime() const;
|
||||
double GetDuration() const;
|
||||
|
||||
// Gets index of the |Representation| with next highest bitrate to the
|
||||
// estimated bandwidth passed in. Returns true if there is at least one
|
||||
// |Representation| with a bitrate lower than |aBandwidth|; otherwise returns
|
||||
// false. Depends on |mRepresentations| being an ordered list.
|
||||
bool GetBestRepForBandwidth(uint32_t aAdaptSetIdx,
|
||||
uint64_t aBandwidth,
|
||||
uint32_t &aRepIdx) const MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// Internal helper functions.
|
||||
AdaptationSet const * GetAdaptationSet(uint32_t const aAdaptSetIdx) const;
|
||||
AdaptationSetType GetAdaptationSetType(nsAString const &mimeType) const;
|
||||
uint32_t GetNumSegments(Representation const* aRep) const;
|
||||
|
||||
// Pointer to the MPD class structure; holds data parsed from the MPD file.
|
||||
nsAutoPtr<MPD> mMpd;
|
||||
};
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
||||
|
||||
#endif /* _NSDASHWEBMODMANAGER_H_ */
|
@ -1,604 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* nsDASHWebMODParser.cpp
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2012 Klagenfurt University
|
||||
*
|
||||
* Created on: May 1, 2012
|
||||
* Based on IsoffMainParser.cpp by:
|
||||
* Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Author:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
* Media Presentation Description (MPD) Parser for WebM On Demand Profile.
|
||||
*
|
||||
* Parses DOM built from MPD XML to verify data and populate MPD classes.
|
||||
* MPD classes used as a metadata source by WebM On Demand Manager.
|
||||
*/
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "prlog.h"
|
||||
#include "nsDASHWebMODParser.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo* gnsDASHWebMODParserLog = nullptr;
|
||||
#define LOG(msg, ...) \
|
||||
PR_LOG(gnsDASHWebMODParserLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHWebMODParser] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) \
|
||||
PR_LOG(gnsDASHWebMODParserLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHWebMODParser] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
nsDASHWebMODParser::nsDASHWebMODParser(nsIDOMElement* aRoot) :
|
||||
mRoot(aRoot)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDASHWebMODParser);
|
||||
#if defined(PR_LOGGING)
|
||||
if(!gnsDASHWebMODParserLog)
|
||||
gnsDASHWebMODParserLog = PR_NewLogModule("nsDASHWebMODParser");
|
||||
#endif
|
||||
LOG1("Created nsDASHWebMODParser");
|
||||
}
|
||||
|
||||
nsDASHWebMODParser::~nsDASHWebMODParser()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsDASHWebMODParser);
|
||||
}
|
||||
|
||||
MPD*
|
||||
nsDASHWebMODParser::Parse()
|
||||
{
|
||||
LOG1("Parsing DOM into MPD objects");
|
||||
nsAutoPtr<MPD> mpd(new MPD());
|
||||
|
||||
nsresult rv = VerifyMPDAttributes();
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
rv = SetMPDBaseUrls(mpd);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
rv = SetPeriods(mpd);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
return mpd.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::VerifyMPDAttributes()
|
||||
{
|
||||
NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// @|type| should be "static".
|
||||
nsAutoString type;
|
||||
nsresult rv = GetAttribute(mRoot, NS_LITERAL_STRING("type"), type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(type.EqualsLiteral("static"), NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// Note: No attributes to be set in MPD object for DASH-WebM OD. This
|
||||
// function used for attribute verification only.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::SetMPDBaseUrls(MPD* aMpd)
|
||||
{
|
||||
NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsCOMPtr<nsIDOMElement> child, nextChild;
|
||||
nsresult rv = mRoot->GetFirstElementChild(getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
int i = 0;
|
||||
#endif
|
||||
while (child) {
|
||||
nsAutoString tagName;
|
||||
rv = child->GetTagName(tagName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (tagName.EqualsLiteral("BaseURL")) {
|
||||
nsAutoString baseUrlStr;
|
||||
rv = child->GetTextContent(baseUrlStr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aMpd->AddBaseUrl(baseUrlStr);
|
||||
LOG("MPD BaseURL #%d: \"%s\"",
|
||||
i++, NS_ConvertUTF16toUTF8(baseUrlStr).get());
|
||||
}
|
||||
rv = child->GetNextElementSibling(getter_AddRefs(nextChild));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
||||
child = nextChild;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::GetTime(nsAString& aTimeStr, double& aTime)
|
||||
{
|
||||
NS_ENSURE_FALSE(aTimeStr.IsEmpty(), NS_ERROR_NOT_INITIALIZED);
|
||||
// Fail if time string is not of the format "PT<time>S".
|
||||
NS_NAMED_LITERAL_STRING(prefix, "PT");
|
||||
NS_NAMED_LITERAL_STRING(suffix, "S");
|
||||
nsAString::const_iterator start, end, prefixStart, prefixEnd,
|
||||
suffixStart, suffixEnd;
|
||||
|
||||
// Search for "PT" at the start.
|
||||
aTimeStr.BeginReading(start);
|
||||
aTimeStr.EndReading(end);
|
||||
prefixStart = start;
|
||||
prefixEnd = end;
|
||||
NS_ENSURE_TRUE(FindInReadable(prefix, prefixStart, prefixEnd),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
NS_ENSURE_TRUE(prefixStart == start, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// Search for "S" after "PT".
|
||||
suffixStart = prefixEnd;
|
||||
suffixEnd = end;
|
||||
NS_ENSURE_TRUE(FindInReadable(suffix, suffixStart, suffixEnd),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
NS_ENSURE_TRUE(suffixStart != prefixEnd, NS_ERROR_ILLEGAL_VALUE);
|
||||
NS_ENSURE_TRUE(suffixEnd == end, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// Parse inner substring for time.
|
||||
const nsAutoString timeSubString(Substring(prefixEnd, suffixStart));
|
||||
LOG("Parsing substring \"%s\" in \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(timeSubString).get(),
|
||||
NS_ConvertUTF16toUTF8(aTimeStr).get());
|
||||
NS_ENSURE_FALSE(timeSubString.IsEmpty(), NS_ERROR_ILLEGAL_VALUE);
|
||||
nsresult rv;
|
||||
aTime = timeSubString.ToDouble(&rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::SetPeriods(MPD* aMpd)
|
||||
{
|
||||
NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsCOMPtr<nsIDOMElement> child, nextChild;
|
||||
nsresult rv = mRoot->GetFirstElementChild(getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
int i = 0;
|
||||
#endif
|
||||
while (child) {
|
||||
nsAutoString tagName;
|
||||
rv = child->GetTagName(tagName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (tagName.EqualsLiteral("Period")) {
|
||||
nsAutoPtr<Period> period(new Period());
|
||||
|
||||
// Get start time and duration
|
||||
nsAutoString value;
|
||||
rv = GetAttribute(child, NS_LITERAL_STRING("start"), value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!value.IsEmpty()) {
|
||||
double startTime = -1;
|
||||
rv = GetTime(value, startTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(0 <= startTime, NS_ERROR_ILLEGAL_VALUE);
|
||||
period->SetStart(startTime);
|
||||
}
|
||||
|
||||
rv = GetAttribute(child, NS_LITERAL_STRING("duration"), value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!value.IsEmpty()) {
|
||||
double duration = -1;
|
||||
rv = GetTime(value, duration);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(0 <= duration, NS_ERROR_ILLEGAL_VALUE);
|
||||
period->SetDuration(duration);
|
||||
}
|
||||
|
||||
bool bIgnoreThisPeriod;
|
||||
rv = SetAdaptationSets(child, period, bIgnoreThisPeriod);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// |Period| should be ignored if its child elems are invalid
|
||||
if (bIgnoreThisPeriod) {
|
||||
LOG1("Ignoring period");
|
||||
} else {
|
||||
aMpd->AddPeriod(period.forget());
|
||||
LOG("Period #%d: added to MPD", i++);
|
||||
}
|
||||
}
|
||||
rv = child->GetNextElementSibling(getter_AddRefs(nextChild));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
child = nextChild;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::ValidateAdaptationSetAttributes(nsIDOMElement* aChild,
|
||||
bool &bAttributesValid)
|
||||
{
|
||||
// Check common attributes first.
|
||||
nsAutoString value;
|
||||
nsresult rv = GetAttribute(aChild, NS_LITERAL_STRING("subsegmentStartsWithSAP"),
|
||||
value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bAttributesValid = (!value.IsEmpty() && value.EqualsLiteral("1"));
|
||||
|
||||
// Now check for video and audio specific attributes.
|
||||
nsAutoString mimeType;
|
||||
if (bAttributesValid) {
|
||||
rv = GetAttribute(aChild, NS_LITERAL_STRING("mimeType"), mimeType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bAttributesValid = !mimeType.IsEmpty();
|
||||
if (!bAttributesValid)
|
||||
LOG1("mimeType not present!");
|
||||
}
|
||||
// Validate attributes for video.
|
||||
if (bAttributesValid && mimeType.EqualsLiteral(VIDEO_WEBM)) {
|
||||
// @|segmentAlignment| is optional
|
||||
if (bAttributesValid) {
|
||||
rv = GetAttribute(aChild, NS_LITERAL_STRING("segmentAlignment"), value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bAttributesValid = (value.IsEmpty() || value.EqualsLiteral("true"));
|
||||
if (!bAttributesValid)
|
||||
LOG1("segmentAlignment not present or invalid!");
|
||||
}
|
||||
if (bAttributesValid) {
|
||||
rv = GetAttribute(aChild, NS_LITERAL_STRING("subsegmentAlignment"),
|
||||
value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bAttributesValid = (!value.IsEmpty() && value.EqualsLiteral("true"));
|
||||
if (!bAttributesValid)
|
||||
LOG1("subsegmentAlignment not present or invalid!");
|
||||
}
|
||||
if (bAttributesValid) {
|
||||
rv = GetAttribute(aChild, NS_LITERAL_STRING("bitstreamSwitching"),
|
||||
value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bAttributesValid = (!value.IsEmpty() && value.EqualsLiteral("true"));
|
||||
if (!bAttributesValid)
|
||||
LOG1("bitstreamSwitching not present or invalid!");
|
||||
}
|
||||
} else if (bAttributesValid && mimeType.EqualsLiteral(AUDIO_WEBM)) {
|
||||
// Validate attributes for audio.
|
||||
} else if (bAttributesValid) {
|
||||
// attributes are not valid since mimeType is wrong
|
||||
bAttributesValid = false;
|
||||
LOG("mimeType is invalid: %s", NS_ConvertUTF16toUTF8(mimeType).get());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::SetAdaptationSets(nsIDOMElement* aPeriodElem,
|
||||
Period* aPeriod,
|
||||
bool &bIgnoreThisPeriod)
|
||||
{
|
||||
NS_ENSURE_ARG(aPeriodElem);
|
||||
NS_ENSURE_ARG(aPeriod);
|
||||
|
||||
// Assume ok until an error is found.
|
||||
bIgnoreThisPeriod = false;
|
||||
|
||||
nsCOMPtr<nsIDOMElement> child, nextChild;
|
||||
nsresult rv = aPeriodElem->GetFirstElementChild(getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
int i = 0;
|
||||
#endif
|
||||
while (child) {
|
||||
nsAutoString tagName;
|
||||
rv = child->GetTagName(tagName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If child is invalid, return and ignore this |Period|
|
||||
if (tagName.EqualsLiteral("SegmentList")
|
||||
|| tagName.EqualsLiteral("SegmentTemplate")) {
|
||||
bIgnoreThisPeriod = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (tagName.EqualsLiteral("AdaptationSet")) {
|
||||
// Ignore this |AdaptationSet| if an attribute is inavlid
|
||||
bool bAttributesValid = false;
|
||||
rv = ValidateAdaptationSetAttributes(child, bAttributesValid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Add |AdaptationSet| to |Period| if attributes and child elems are ok
|
||||
if (bAttributesValid) {
|
||||
nsAutoPtr<AdaptationSet> adaptationSet(new AdaptationSet());
|
||||
|
||||
// Attribute check already done; set correct value for this profile
|
||||
adaptationSet->EnableBitstreamSwitching(true);
|
||||
|
||||
nsAutoString mimeType;
|
||||
rv = GetAttribute(child, NS_LITERAL_STRING("mimeType"), mimeType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!mimeType.IsEmpty()) {
|
||||
adaptationSet->SetMIMEType(mimeType);
|
||||
}
|
||||
|
||||
// TODO width
|
||||
|
||||
// TODO height
|
||||
|
||||
// TODO codecs
|
||||
|
||||
bool bIgnoreThisAdaptSet = false;
|
||||
rv = SetRepresentations(child, adaptationSet, bIgnoreThisAdaptSet);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!bIgnoreThisAdaptSet) {
|
||||
LOG("AdaptationSet #%d: mimeType:%s width:%d height:%d codecs:%s",
|
||||
i, NS_ConvertUTF16toUTF8(mimeType).get(),
|
||||
adaptationSet->GetWidth(), adaptationSet->GetHeight(), "");
|
||||
aPeriod->AddAdaptationSet(adaptationSet.forget());
|
||||
LOG("AdaptationSet #%d: added to Period", i++);
|
||||
}
|
||||
}
|
||||
}
|
||||
rv = child->GetNextElementSibling(getter_AddRefs(nextChild));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
child = nextChild;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::SetRepresentations(nsIDOMElement* aAdaptSetElem,
|
||||
AdaptationSet* aAdaptationSet,
|
||||
bool &bIgnoreThisAdaptSet)
|
||||
{
|
||||
NS_ENSURE_ARG(aAdaptSetElem);
|
||||
NS_ENSURE_ARG(aAdaptationSet);
|
||||
|
||||
// Assume ok until an error is found.
|
||||
bIgnoreThisAdaptSet = false;
|
||||
|
||||
nsCOMPtr<nsIDOMElement> child, nextChild;
|
||||
nsresult rv = aAdaptSetElem->GetFirstElementChild(getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
int i = 0;
|
||||
#endif
|
||||
bIgnoreThisAdaptSet = false;
|
||||
while (child) {
|
||||
nsAutoString tagName;
|
||||
rv = child->GetTagName(tagName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If child is invalid, return and ignore this |Period|
|
||||
if (tagName.EqualsLiteral("SegmentList")
|
||||
|| tagName.EqualsLiteral("SegmentTemplate")) {
|
||||
bIgnoreThisAdaptSet = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (tagName.EqualsLiteral("Representation")) {
|
||||
|
||||
nsAutoPtr<Representation> representation(new Representation());
|
||||
|
||||
nsAutoString value;
|
||||
rv = GetAttribute(child, NS_LITERAL_STRING("width"), value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if(!value.IsEmpty()) {
|
||||
representation->SetWidth(value.ToInteger(&rv));
|
||||
}
|
||||
|
||||
rv = GetAttribute(child, NS_LITERAL_STRING("height"), value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if(!value.IsEmpty()) {
|
||||
representation->SetHeight(value.ToInteger(&rv));
|
||||
}
|
||||
|
||||
rv = GetAttribute(child, NS_LITERAL_STRING("bandwidth"), value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if(!value.IsEmpty()) {
|
||||
representation->SetBitrate(value.ToInteger(&rv));
|
||||
}
|
||||
|
||||
LOG("Representation #%d: width:%d height:%d bitrate:%d",
|
||||
i, representation->GetWidth(),
|
||||
representation->GetHeight(),
|
||||
representation->GetBitrate());
|
||||
|
||||
// Get |BaseURL| elements
|
||||
bool bIgnoreThisRep;
|
||||
SetRepresentationBaseUrls(child, representation, bIgnoreThisRep);
|
||||
|
||||
// Get |SegmentBase| elements
|
||||
if (!bIgnoreThisRep)
|
||||
SetRepSegmentBase(child, representation, bIgnoreThisRep);
|
||||
|
||||
if (!bIgnoreThisRep) {
|
||||
aAdaptationSet->AddRepresentation(representation.forget());
|
||||
LOG("Representation #%d: added to AdaptationSet", i++);
|
||||
}
|
||||
}
|
||||
rv = child->GetNextElementSibling(getter_AddRefs(nextChild));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
child = nextChild;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::SetRepresentationBaseUrls(nsIDOMElement* aRepElem,
|
||||
Representation* aRep,
|
||||
bool &bIgnoreThisRep)
|
||||
{
|
||||
NS_ENSURE_ARG(aRepElem);
|
||||
NS_ENSURE_ARG(aRep);
|
||||
|
||||
nsCOMPtr<nsIDOMElement> child, nextChild;
|
||||
nsresult rv = aRepElem->GetFirstElementChild(getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
int i = 0;
|
||||
#endif
|
||||
// Ignore if no |BaseURL| elems; assume none exist until discovered.
|
||||
bIgnoreThisRep = true;
|
||||
while (child) {
|
||||
nsAutoString tagName;
|
||||
rv = child->GetTagName(tagName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (tagName.EqualsLiteral("BaseURL")) {
|
||||
bIgnoreThisRep = false;
|
||||
nsAutoString baseUrlStr;
|
||||
rv = child->GetTextContent(baseUrlStr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aRep->AddBaseUrl(baseUrlStr);
|
||||
LOG("BaseURL #%d: \"%s\" added to Representation",
|
||||
i++, NS_ConvertUTF16toUTF8(baseUrlStr).get());
|
||||
}
|
||||
rv = child->GetNextElementSibling(getter_AddRefs(nextChild));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
child = nextChild;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::SetRepSegmentBase(nsIDOMElement* aRepElem,
|
||||
Representation* aRep,
|
||||
bool &bIgnoreThisRep)
|
||||
{
|
||||
NS_ENSURE_ARG(aRepElem);
|
||||
NS_ENSURE_ARG(aRep);
|
||||
|
||||
nsCOMPtr<nsIDOMElement> child, nextChild;
|
||||
nsresult rv = aRepElem->GetFirstElementChild(getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
int i = 0;
|
||||
#endif
|
||||
// Ignore if no |SegmentBase| elems; assume none exist until discovered.
|
||||
bIgnoreThisRep = true;
|
||||
while (child) {
|
||||
nsAutoString tagName;
|
||||
rv = child->GetTagName(tagName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (tagName.EqualsLiteral("SegmentBase")) {
|
||||
bIgnoreThisRep = false;
|
||||
bool bIgnoreThisSegBase = false;
|
||||
|
||||
nsAutoPtr<SegmentBase> segmentBase(new SegmentBase());
|
||||
|
||||
nsAutoString value;
|
||||
rv = GetAttribute(child, NS_LITERAL_STRING("indexRange"), value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if(!value.IsEmpty()) {
|
||||
segmentBase->SetIndexRange(value);
|
||||
} else {
|
||||
bIgnoreThisRep = true;
|
||||
bIgnoreThisSegBase = true;
|
||||
}
|
||||
|
||||
if (!bIgnoreThisSegBase) {
|
||||
SetSegmentBaseInit(child, segmentBase, bIgnoreThisSegBase);
|
||||
}
|
||||
|
||||
if (!bIgnoreThisSegBase) {
|
||||
aRep->SetSegmentBase(segmentBase.forget());
|
||||
LOG("SegmentBase #%d: added to Representation", i++);
|
||||
}
|
||||
break;
|
||||
}
|
||||
rv = child->GetNextElementSibling(getter_AddRefs(nextChild));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
child = nextChild;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::SetSegmentBaseInit(nsIDOMElement* aSegBaseElem,
|
||||
SegmentBase* aSegBase,
|
||||
bool &bIgnoreThisSegBase)
|
||||
{
|
||||
NS_ENSURE_ARG(aSegBaseElem);
|
||||
NS_ENSURE_ARG(aSegBase);
|
||||
|
||||
nsCOMPtr<nsIDOMElement> child, nextChild;
|
||||
nsresult rv = aSegBaseElem->GetFirstElementChild(getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
int i = 0;
|
||||
#endif
|
||||
// Ignore if no |Initialisation| elems; assume none exist until discovered.
|
||||
bIgnoreThisSegBase = true;
|
||||
while (child) {
|
||||
nsAutoString tagName;
|
||||
rv = child->GetTagName(tagName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// XXX Spec spells with 's'; WebM Project defn spells with 'z': accept both
|
||||
if (tagName.EqualsLiteral("Initialisation")
|
||||
|| tagName.EqualsLiteral("Initialization")) {
|
||||
bIgnoreThisSegBase = false;
|
||||
|
||||
nsAutoString value;
|
||||
rv = GetAttribute(child, NS_LITERAL_STRING("range"), value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if(!value.IsEmpty()) {
|
||||
aSegBase->SetInitRange(value);
|
||||
LOG("Initialisation #%d: added to SegmentBase", i++);
|
||||
} else {
|
||||
bIgnoreThisSegBase = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
rv = child->GetNextElementSibling(getter_AddRefs(nextChild));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
child = nextChild;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHWebMODParser::GetAttribute(nsIDOMElement* aElem,
|
||||
const nsAString& aAttribute,
|
||||
nsAString& aValue)
|
||||
{
|
||||
bool bAttributePresent;
|
||||
nsresult rv = aElem->HasAttribute(aAttribute, &bAttributePresent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!bAttributePresent)
|
||||
aValue.AssignLiteral("");
|
||||
else {
|
||||
rv = aElem->GetAttribute(aAttribute, aValue);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
@ -1,159 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/*
|
||||
* nsDASHWebMODParser.h
|
||||
*****************************************************************************
|
||||
* Copyrigh(C) 2010 - 2012 Klagenfurt University
|
||||
*
|
||||
* Created on: May 1, 2012
|
||||
* Based on IsoffMainParser.h by:
|
||||
* Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
|
||||
* Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
|
||||
* Author:
|
||||
* Steve Workman <sworkman@mozilla.com>
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
* Media Presentation Description (MPD) Parser for WebM On Demand Profile.
|
||||
*
|
||||
* Parses DOM built from MPD XML to verify data and populate MPD classes.
|
||||
* MPD classes used as a metadata source by WebM On Demand Manager.
|
||||
*
|
||||
*
|
||||
* General:
|
||||
* -- Ignore all elements with @|xlink|.
|
||||
* -- @|startWithSAP| XXX examine later - defines "Segments starting with
|
||||
* media stream access points".
|
||||
* -- @|mimeType| for elements should be 'video/webm' or 'audio/webm'.
|
||||
*
|
||||
* |MPD|:
|
||||
* -- May have |BaseURL|.
|
||||
* -- @|type| = 'static' only.
|
||||
*
|
||||
* |Period|:
|
||||
* -- no |SegmentList| or |SegmentTemplate|; not just ignored, they shouldn't be
|
||||
* present.
|
||||
*
|
||||
* |AdaptationSet|:
|
||||
* -- ignore this element if it has a |SegmentList| or |SegmentTemplate|.
|
||||
* -- ignore this element if @|subsegmentAlignment| is not present or is set
|
||||
* to false.
|
||||
* To enable stream switching:
|
||||
* -- @|subsegmentStartsWithSAP| = 1.
|
||||
* -- @|segmentAlignment| = true.
|
||||
* -- @|bitstreamSwitching| = true.
|
||||
* -- Can have @|width| and @|height|.
|
||||
* -- Can have @|id|.
|
||||
* -- Can have @|codecs|, but this should match appropriately with @|mimeType|.
|
||||
*
|
||||
* |Representation|:
|
||||
* -- should contain only one |Segment|, which should be 'Self-Initializing
|
||||
* Indexed Media Segment'. Note: |Segment|s are generated from |BaseURL|
|
||||
* elements within a |Representation|.
|
||||
* -- ignore if @|profiles| is not the same as MPD's @|profiles|.
|
||||
* -- ignore this element if it has a |SegmentList| or |SegmentTemplate|.
|
||||
* -- ignore if it does NOT have a |BaseURL|.
|
||||
* -- ignore if @|subsegmentStartsWithSAP| != 1 and has sibling |Representation|s
|
||||
* in |AdaptationSet|.
|
||||
* -- no |IndexSegment|.
|
||||
*
|
||||
* |BaseURL|:
|
||||
* -- no specifics.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DASHWEBMODPARSER_H_
|
||||
#define DASHWEBMODPARSER_H_
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "IMPDParser.h"
|
||||
#include "MPD.h"
|
||||
#include "Period.h"
|
||||
#include "AdaptationSet.h"
|
||||
#include "Representation.h"
|
||||
#include "SegmentBase.h"
|
||||
|
||||
class nsIDOMElement;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class nsDASHWebMODParser : public IMPDParser
|
||||
{
|
||||
public:
|
||||
nsDASHWebMODParser(nsIDOMElement* aRoot);
|
||||
virtual ~nsDASHWebMODParser();
|
||||
|
||||
// Inherited from IMPDParser.
|
||||
MPD* Parse();
|
||||
|
||||
private:
|
||||
// Parses MPD tag attributes from the DOM.
|
||||
nsresult VerifyMPDAttributes();
|
||||
|
||||
// Parses the |BaseURL| to be used for all other URLs.
|
||||
nsresult SetMPDBaseUrls(MPD* aMpd);
|
||||
|
||||
// Parses all |Period|s in the DOM of the media presentation.
|
||||
nsresult SetPeriods(MPD* aMpd);
|
||||
|
||||
// Parses |AdaptationSet|s of media in the DOM node for the given |Period|.
|
||||
nsresult SetAdaptationSets(nsIDOMElement* aPeriodNode,
|
||||
Period* aPeriod,
|
||||
bool &bIgnoreThisRep);
|
||||
|
||||
// Validates the attributes for the given |AdaptationSet| of media.
|
||||
nsresult ValidateAdaptationSetAttributes(nsIDOMElement* aChild,
|
||||
bool &bAttributesValid);
|
||||
|
||||
// Parses |Representation|s in the DOM for the given |AdaptationSet|.
|
||||
nsresult SetRepresentations(nsIDOMElement* aAdaptationSetNode,
|
||||
AdaptationSet* aAdaptationSet,
|
||||
bool &bIgnoreThisRep);
|
||||
|
||||
// Parses |BaseURLs| for the given media |Representation|.
|
||||
// Note: in practise, this is combined with the |BaseURL| for the |MPD|.
|
||||
nsresult SetRepresentationBaseUrls(nsIDOMElement* aRepNode,
|
||||
Representation* aRep,
|
||||
bool &bIgnoreThisRep);
|
||||
|
||||
// Parses the |SegmentBase| for the media |Representation|.
|
||||
// For WebM OD, there is one |SegmentBase| and one |Segment| per media
|
||||
// |Representation|.
|
||||
nsresult SetRepSegmentBase(nsIDOMElement* aRepElem,
|
||||
Representation* aRep,
|
||||
bool &bIgnoreThisRep);
|
||||
// Parses the |InitSegment| for the |SegmentBase|.
|
||||
// For WebM OD, the |InitSegment| is a range of bytes for each |SegmentBase|.
|
||||
nsresult SetSegmentBaseInit(nsIDOMElement* aSegBaseElem,
|
||||
SegmentBase* aSegBase,
|
||||
bool &bIgnoreThisSegBase);
|
||||
|
||||
// Gets an attribute's value from a DOM element.
|
||||
nsresult GetAttribute(nsIDOMElement* aElem,
|
||||
const nsAString& aAttribute,
|
||||
nsAString& aValue);
|
||||
|
||||
// Converts a subtring "<time>" in a string of format "PT<time>S" to a double.
|
||||
nsresult GetTime(nsAString& aTimeStr, double& aTime);
|
||||
|
||||
// The root DOM element of the MPD; populated after reading the XML file; used
|
||||
// to populate the MPD class structure.
|
||||
nsCOMPtr<nsIDOMElement> mRoot;
|
||||
};
|
||||
|
||||
}//namespace net
|
||||
}//namespace mozilla
|
||||
|
||||
#endif /* DASHWEBMODPARSER_H_ */
|
@ -145,9 +145,6 @@
|
||||
#define VIDEO_WEBM "video/webm"
|
||||
#define VIDEO_3GPP "video/3gpp"
|
||||
#define APPLICATION_OGG "application/ogg"
|
||||
#ifdef MOZ_DASH
|
||||
#define APPLICATION_DASH "application/dash+xml"
|
||||
#endif
|
||||
|
||||
/* x-uuencode-apple-single. QuickMail made me do this. */
|
||||
#define UUENCODE_APPLE_SINGLE "x-uuencode-apple-single"
|
||||
|
@ -29,9 +29,6 @@ if CONFIG['NECKO_WIFI']:
|
||||
|
||||
PARALLEL_DIRS += ['locales']
|
||||
|
||||
if CONFIG['MOZ_DASH']:
|
||||
PARALLEL_DIRS += ['dash']
|
||||
|
||||
DIRS += ['build']
|
||||
TEST_TOOL_DIRS += ['test']
|
||||
|
||||
|
@ -412,9 +412,6 @@ static nsDefaultMimeTypeEntry defaultMimeEntries [] =
|
||||
{ VIDEO_WEBM, "webm" },
|
||||
{ AUDIO_WEBM, "webm" },
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
{ APPLICATION_DASH, "mpd" },
|
||||
#endif
|
||||
#if defined(MOZ_GSTREAMER) || defined(MOZ_WMF)
|
||||
{ VIDEO_MP4, "mp4" },
|
||||
{ AUDIO_MP4, "m4a" },
|
||||
@ -497,9 +494,6 @@ static nsExtraMimeTypeEntry extraMimeEntries [] =
|
||||
#endif
|
||||
{ VIDEO_WEBM, "webm", "Web Media Video" },
|
||||
{ AUDIO_WEBM, "webm", "Web Media Audio" },
|
||||
#ifdef MOZ_DASH
|
||||
{ APPLICATION_DASH, "mpd", "DASH Media Presentation Description" },
|
||||
#endif
|
||||
{ AUDIO_MP3, "mp3", "MPEG Audio" },
|
||||
{ VIDEO_MP4, "mp4", "MPEG-4 Video" },
|
||||
{ AUDIO_MP4, "m4a", "MPEG-4 Audio" },
|
||||
|
Loading…
Reference in New Issue
Block a user