mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 734546: Add DASH Decoders and Readers r=cpearce r=ted
This commit is contained in:
parent
b2049ebc9e
commit
1de10bcca3
20
configure.in
20
configure.in
@ -4181,6 +4181,7 @@ MOZ_SAMPLE_TYPE_S16=
|
||||
MOZ_MEDIA=
|
||||
MOZ_OPUS=1
|
||||
MOZ_WEBM=1
|
||||
MOZ_DASH=
|
||||
MOZ_WEBRTC=1
|
||||
MOZ_SRTP=
|
||||
MOZ_WEBRTC_SIGNALING=
|
||||
@ -5271,6 +5272,24 @@ if test -n "$MOZ_WEBM"; then
|
||||
MOZ_VP8=1
|
||||
fi;
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable DASH-WebM support
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(dash,
|
||||
[ --enable-dash Enable support for DASH-WebM],
|
||||
MOZ_DASH=1,
|
||||
MOZ_DASH=)
|
||||
|
||||
if test -n "$MOZ_DASH"; then
|
||||
if test -n "$MOZ_WEBM"; then
|
||||
AC_DEFINE(MOZ_DASH)
|
||||
else
|
||||
dnl Fail if WebM is not enabled as well as DASH.
|
||||
AC_MSG_ERROR([WebM is currently disabled and must be enabled for DASH
|
||||
to work.])
|
||||
fi
|
||||
fi;
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable media plugin support
|
||||
dnl ========================================================
|
||||
@ -8523,6 +8542,7 @@ AC_SUBST(MOZ_VORBIS)
|
||||
AC_SUBST(MOZ_TREMOR)
|
||||
AC_SUBST(MOZ_OPUS)
|
||||
AC_SUBST(MOZ_WEBM)
|
||||
AC_SUBST(MOZ_DASH)
|
||||
AC_SUBST(MOZ_MEDIA_PLUGINS)
|
||||
AC_SUBST(MOZ_OMX_PLUGIN)
|
||||
AC_SUBST(MOZ_VP8_ERROR_CONCEALMENT)
|
||||
|
@ -33,6 +33,9 @@ typedef uint16_t nsMediaReadyState;
|
||||
namespace mozilla {
|
||||
class MediaResource;
|
||||
}
|
||||
#ifdef MOZ_DASH
|
||||
class nsDASHDecoder;
|
||||
#endif
|
||||
|
||||
class nsHTMLMediaElement : public nsGenericHTMLElement,
|
||||
public nsIObserver
|
||||
@ -46,6 +49,10 @@ public:
|
||||
|
||||
typedef nsDataHashtable<nsCStringHashKey, nsCString> MetadataTags;
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
friend class nsDASHDecoder;
|
||||
#endif
|
||||
|
||||
enum CanPlayStatus {
|
||||
CANPLAY_NO,
|
||||
CANPLAY_MAYBE,
|
||||
@ -319,6 +326,12 @@ public:
|
||||
static bool IsMediaPluginsType(const nsACString& aType);
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
static bool IsDASHEnabled();
|
||||
static bool IsDASHMPDType(const nsACString& aType);
|
||||
static const char gDASHMPDTypes[1][21];
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get the mime type for this element.
|
||||
*/
|
||||
|
@ -91,6 +91,9 @@
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsMediaOmxDecoder.h"
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
#include "nsDASHDecoder.h"
|
||||
#endif
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
static PRLogModuleInfo* gMediaElementLog;
|
||||
@ -2221,6 +2224,37 @@ nsHTMLMediaElement::IsMediaPluginsType(const nsACString& aType)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
/* static */
|
||||
const char nsHTMLMediaElement::gDASHMPDTypes[1][21] = {
|
||||
"application/dash+xml"
|
||||
};
|
||||
|
||||
/* static */
|
||||
bool
|
||||
nsHTMLMediaElement::IsDASHEnabled()
|
||||
{
|
||||
return Preferences::GetBool("media.dash.enabled");
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
nsHTMLMediaElement::IsDASHMPDType(const nsACString& aType)
|
||||
{
|
||||
if (!IsDASHEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < ArrayLength(gDASHMPDTypes); ++i) {
|
||||
if (aType.EqualsASCII(gDASHMPDTypes[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* static */
|
||||
nsHTMLMediaElement::CanPlayStatus
|
||||
nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType,
|
||||
@ -2250,6 +2284,13 @@ nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType,
|
||||
return CANPLAY_YES;
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
if (IsDASHMPDType(nsDependentCString(aMIMEType))) {
|
||||
// DASH manifest uses WebM codecs only.
|
||||
*aCodecList = gWebMCodecs;
|
||||
return CANPLAY_YES;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_GSTREAMER
|
||||
if (IsH264Type(nsDependentCString(aMIMEType))) {
|
||||
@ -2439,6 +2480,15 @@ nsHTMLMediaElement::CreateDecoder(const nsACString& aType)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
if (IsDASHMPDType(aType)) {
|
||||
nsRefPtr<nsDASHDecoder> decoder = new nsDASHDecoder();
|
||||
if (decoder->Init(this)) {
|
||||
return decoder.forget();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_GSTREAMER
|
||||
if (IsH264Type(aType)) {
|
||||
nsRefPtr<nsGStreamerDecoder> decoder = new nsGStreamerDecoder();
|
||||
|
@ -84,6 +84,10 @@ ifdef MOZ_GSTREAMER
|
||||
PARALLEL_DIRS += gstreamer
|
||||
endif
|
||||
|
||||
ifdef MOZ_DASH
|
||||
PARALLEL_DIRS += dash
|
||||
endif
|
||||
|
||||
ifdef MOZ_MEDIA_PLUGINS
|
||||
PARALLEL_DIRS += plugins
|
||||
endif
|
||||
|
@ -981,7 +981,6 @@ ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
|
||||
|
||||
if (mByteRangeDownloads) {
|
||||
// Query decoder for chunk containing desired offset.
|
||||
// XXX Implement |nsDASHRepDecoder|::|GetByteRange| in future patch.
|
||||
nsresult rv;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mSeekOffsetMonitor);
|
||||
|
44
content/media/dash/Makefile.in
Normal file
44
content/media/dash/Makefile.in
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- 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>
|
||||
|
||||
DEPTH := @DEPTH@
|
||||
topsrcdir := @top_srcdir@
|
||||
srcdir := @srcdir@
|
||||
VPATH := @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE := content
|
||||
LIBRARY_NAME := gkcondash_s
|
||||
LIBXUL_LIBRARY := 1
|
||||
|
||||
EXPORTS := \
|
||||
nsDASHDecoder.h \
|
||||
nsDASHRepDecoder.h \
|
||||
nsDASHReader.h \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS := \
|
||||
nsDASHDecoder.cpp \
|
||||
nsDASHRepDecoder.cpp \
|
||||
nsDASHReader.cpp \
|
||||
$(NULL)
|
||||
|
||||
FORCE_STATIC_LIB := 1
|
||||
|
||||
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)
|
725
content/media/dash/nsDASHDecoder.cpp
Normal file
725
content/media/dash/nsDASHDecoder.cpp
Normal file
@ -0,0 +1,725 @@
|
||||
/* -*- 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.
|
||||
*
|
||||
* Interaction with nsBuiltinDecoderStateMachine, nsHTMLMediaElement,
|
||||
* ChannelMediaResource and sub-decoders (nsWebMDecoder).
|
||||
*
|
||||
*
|
||||
* nsBuiltinDecoderStateMachine nsHTMLMediaElement
|
||||
* 1 / \ 1 / 1
|
||||
* / \ /
|
||||
* 1 / \ 1 / 1
|
||||
* nsDASHReader ------ nsDASHDecoder ------------ ChannelMediaResource
|
||||
* |1 1 1 |1 \1 (for MPD Manifest)
|
||||
* | | ------------
|
||||
* |* |* \*
|
||||
* nsWebMReader ------- nsDASHRepDecoder ------- ChannelMediaResource
|
||||
* 1 1 1 1 (for media streams)
|
||||
*
|
||||
* One decoder and state-machine, as with current, non-DASH decoders.
|
||||
*
|
||||
* DASH adds multiple readers, decoders and resources, in order to manage
|
||||
* download and decode of the MPD manifest and individual media streams.
|
||||
*
|
||||
* Rep/|Representation| is for an individual media stream, e.g. audio
|
||||
* nsDASHRepDecoder is the decoder for a rep/|Representation|.
|
||||
*
|
||||
* FLOW
|
||||
*
|
||||
* 1 - Download and parse the MPD (DASH XML-based manifest).
|
||||
*
|
||||
* Media element creates new |nsDASHDecoder| object:
|
||||
* member var initialization to default values, including a/v sub-decoders.
|
||||
* nsBuiltinDecoder and nsMediaDecoder constructors are called.
|
||||
* nsBuiltinDecoder::Init() is called.
|
||||
*
|
||||
* Media element creates new |ChannelMediaResource|:
|
||||
* used to download MPD manifest.
|
||||
*
|
||||
* Media element calls |nsDASHDecoder|->Load() to download the MPD file:
|
||||
* creates an |nsDASHReader| object to forward calls to multiple
|
||||
* nsWebMReaders (corresponding to MPD |Representation|s i.e. streams).
|
||||
* Note: 1 |nsDASHReader| per DASH/WebM MPD.
|
||||
*
|
||||
* also calls |ChannelMediaResource|::Open().
|
||||
* uses nsHttpChannel to download MPD; notifies nsDASHDecoder.
|
||||
*
|
||||
* Meanwhile, back in |nsDASHDecoder|->Load():
|
||||
* nsBuiltinDecoderStateMachine is created.
|
||||
* has ref to |nsDASHReader| object.
|
||||
* state machine is scheduled.
|
||||
*
|
||||
* Media element finishes decoder setup:
|
||||
* element added to media URI table etc.
|
||||
*
|
||||
* -- At this point, objects are waiting on HTTP returning MPD data.
|
||||
*
|
||||
* MPD Download (Async |ChannelMediaResource| channel callbacks):
|
||||
* calls nsDASHDecoder::|NotifyDownloadEnded|().
|
||||
* nsDASHDecoder parses MPD XML to DOM to MPD classes.
|
||||
* gets |Segment| URLs from MPD for audio and video streams.
|
||||
* creates |nsIChannel|s, |ChannelMediaResource|s.
|
||||
* stores resources as member vars (to forward function calls later).
|
||||
* creates |nsWebMReader|s and |nsDASHRepDecoder|s.
|
||||
* DASHreader creates |nsWebMReader|s.
|
||||
* |Representation| decoders are connected to the |ChannelMediaResource|s.
|
||||
*
|
||||
* |nsDASHDecoder|->|LoadRepresentations|() starts download and decode.
|
||||
*
|
||||
*
|
||||
* 2 - Media Stream, Byte Range downloads.
|
||||
*
|
||||
* -- At this point the Segment media stream downloads are managed by
|
||||
* individual |ChannelMediaResource|s and |nsWebMReader|s.
|
||||
* A single |nsDASHDecoder| and |nsBuiltinDecoderStateMachine| manage them
|
||||
* and communicate to |nsHTMLMediaElement|.
|
||||
*
|
||||
* Each |nsDASHRepDecoder| gets init range and index range from its MPD
|
||||
* |Representation|. |nsDASHRepDecoder| uses ChannelMediaResource to start the
|
||||
* byte range downloads, calling |OpenByteRange| with a |MediaByteRange|
|
||||
* object.
|
||||
* Once the init and index segments have been downloaded and |ReadMetadata| has
|
||||
* completed, each |nsWebMReader| notifies it's peer |nsDASHRepDecoder|.
|
||||
* Note: the decoder must wait until index data is parsed because it needs to
|
||||
* get the offsets of the subsegments (WebM clusters) from the media file
|
||||
* itself.
|
||||
* Since byte ranges for subsegments are obtained, |nsDASHRepdecoder| continues
|
||||
* downloading the files in byte range chunks.
|
||||
*
|
||||
* XXX Note that this implementation of nsDASHRepDecoder is focused on DASH
|
||||
* WebM On Demand profile: on the todo list is an action item to make this
|
||||
* more abstract.
|
||||
*
|
||||
* Note on |Seek|: Currently, |nsMediaCache| requires that seeking start at the
|
||||
* beginning of the block in which the desired offset would be
|
||||
* found. As such, when |ChannelMediaResource| does a seek
|
||||
* using DASH WebM subsegments (clusters), it requests a start
|
||||
* offset that corresponds to the beginning of the block, not
|
||||
* the start offset of the cluster. For DASH Webm, which has
|
||||
* media encoded in single files, this is fine. Future work on
|
||||
* other profiles will require this to be re-examined.
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
#include <prdtoa.h>
|
||||
#include "nsIURI.h"
|
||||
#include "nsIFileURL.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsICachingChannel.h"
|
||||
#include "nsBuiltinDecoderStateMachine.h"
|
||||
#include "nsWebMDecoder.h"
|
||||
#include "nsWebMReader.h"
|
||||
#include "nsDASHReader.h"
|
||||
#include "nsDASHMPDParser.h"
|
||||
#include "nsDASHRepDecoder.h"
|
||||
#include "nsDASHDecoder.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gBuiltinDecoderLog;
|
||||
#define LOG(msg, ...) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHDecoder] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHDecoder] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
nsDASHDecoder::nsDASHDecoder() :
|
||||
nsBuiltinDecoder(),
|
||||
mNotifiedLoadAborted(false),
|
||||
mBuffer(nullptr),
|
||||
mBufferLength(0),
|
||||
mMPDReaderThread(nullptr),
|
||||
mPrincipal(nullptr),
|
||||
mDASHReader(nullptr),
|
||||
mAudioRepDecoder(nullptr),
|
||||
mVideoRepDecoder(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDASHDecoder);
|
||||
}
|
||||
|
||||
nsDASHDecoder::~nsDASHDecoder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsDASHDecoder);
|
||||
}
|
||||
|
||||
nsDecoderStateMachine*
|
||||
nsDASHDecoder::CreateStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
return new nsBuiltinDecoderStateMachine(this, mDASHReader);
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHDecoder::ReleaseStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
||||
|
||||
// Since state machine owns mDASHReader, remove reference to it.
|
||||
mDASHReader = nullptr;
|
||||
|
||||
nsBuiltinDecoder::ReleaseStateMachine();
|
||||
for (uint i = 0; i < mAudioRepDecoders.Length(); i++) {
|
||||
mAudioRepDecoders[i]->ReleaseStateMachine();
|
||||
}
|
||||
for (uint i = 0; i < mVideoRepDecoders.Length(); i++) {
|
||||
mVideoRepDecoders[i]->ReleaseStateMachine();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHDecoder::Load(MediaResource* aResource,
|
||||
nsIStreamListener** aStreamListener,
|
||||
nsMediaDecoder* aCloneDonor)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
mDASHReader = new nsDASHReader(this);
|
||||
|
||||
nsresult rv = OpenResource(aResource, aStreamListener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mDecoderStateMachine = CreateStateMachine();
|
||||
if (!mDecoderStateMachine) {
|
||||
LOG1("Failed to create state machine!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHDecoder::NotifyDownloadEnded(nsresult aStatus)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
// Should be no download ended notification if MPD Manager exists.
|
||||
if (mMPDManager) {
|
||||
LOG("Network Error! Repeated MPD download notification but MPD Manager "
|
||||
"[%p] already exists!", mMPDManager.get());
|
||||
NetworkError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
LOG1("MPD downloaded.");
|
||||
|
||||
// mPrincipal must be set on main thread before dispatch to parser thread.
|
||||
mPrincipal = GetCurrentPrincipal();
|
||||
|
||||
// Create reader thread for |ChannelMediaResource|::|Read|.
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &nsDASHDecoder::ReadMPDBuffer);
|
||||
NS_ENSURE_TRUE(event, );
|
||||
|
||||
nsresult rv = NS_NewNamedThread("DASH MPD Reader",
|
||||
getter_AddRefs(mMPDReaderThread),
|
||||
event,
|
||||
MEDIA_THREAD_STACK_SIZE);
|
||||
if (NS_FAILED(rv) || !mMPDReaderThread) {
|
||||
LOG("Error creating MPD reader thread: rv[%x] thread [%p].",
|
||||
rv, mMPDReaderThread.get());
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
} else if (aStatus == NS_BINDING_ABORTED) {
|
||||
LOG("MPD download has been cancelled by the user: aStatus [%x].", aStatus);
|
||||
if (mElement) {
|
||||
mElement->LoadAborted();
|
||||
}
|
||||
return;
|
||||
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
|
||||
LOG("Network error trying to download MPD: aStatus [%x].", aStatus);
|
||||
NetworkError();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHDecoder::ReadMPDBuffer()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread.");
|
||||
|
||||
LOG1("Started reading from the MPD buffer.");
|
||||
|
||||
int64_t length = mResource->GetLength();
|
||||
if (length <= 0 || length > DASH_MAX_MPD_SIZE) {
|
||||
LOG("MPD is larger than [%d]MB.", DASH_MAX_MPD_SIZE/(1024*1024));
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
mBuffer = new char[length];
|
||||
|
||||
uint32_t count = 0;
|
||||
nsresult rv = mResource->Read(mBuffer, length, &count);
|
||||
// By this point, all bytes should be available for reading.
|
||||
if (NS_FAILED(rv) || count != length) {
|
||||
LOG("Error reading MPD buffer: rv [%x] count [%d] length [%d].",
|
||||
rv, count, length);
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
// Store buffer length for processing on main thread.
|
||||
mBufferLength = static_cast<uint32_t>(length);
|
||||
|
||||
LOG1("Finished reading MPD buffer; back to main thread for parsing.");
|
||||
|
||||
// Dispatch event to Main thread to parse MPD buffer.
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &nsDASHDecoder::OnReadMPDBufferCompleted);
|
||||
rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error dispatching parse event to main thread: rv[%x]", rv);
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHDecoder::OnReadMPDBufferCompleted()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shutdown the thread.
|
||||
if (!mMPDReaderThread) {
|
||||
LOG1("Error: MPD reader thread does not exist!");
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
nsresult rv = mMPDReaderThread->Shutdown();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("MPD reader thread did not shutdown correctly! rv [%x]", rv);
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
mMPDReaderThread = nullptr;
|
||||
|
||||
// Close the MPD resource.
|
||||
rv = mResource ? mResource->Close() : NS_ERROR_NULL_POINTER;
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Media Resource did not close correctly! rv [%x]", rv);
|
||||
NetworkError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Start parsing the MPD data and loading the media.
|
||||
rv = ParseMPDBuffer();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error parsing MPD buffer! rv [%x]", rv);
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
rv = CreateRepDecoders();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error creating decoders for Representations! rv [%x]", rv);
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
rv = LoadRepresentations();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error loading Representations! rv [%x]", rv);
|
||||
NetworkError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify reader that it can start reading metadata. Sub-readers will still
|
||||
// block until sub-resources have downloaded data into the media cache.
|
||||
mDASHReader->ReadyToReadMetadata();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHDecoder::ParseMPDBuffer()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE(mBuffer, NS_ERROR_NULL_POINTER);
|
||||
|
||||
LOG1("Started parsing the MPD buffer.");
|
||||
|
||||
// Parse MPD buffer and get root DOM element.
|
||||
nsAutoPtr<nsDASHMPDParser> parser;
|
||||
parser = new nsDASHMPDParser(mBuffer.forget(), mBufferLength, mPrincipal,
|
||||
mResource->URI());
|
||||
mozilla::net::DASHMPDProfile profile;
|
||||
parser->Parse(getter_Transfers(mMPDManager), &profile);
|
||||
mBuffer = nullptr;
|
||||
NS_ENSURE_TRUE(mMPDManager, NS_ERROR_NULL_POINTER);
|
||||
|
||||
LOG("Finished parsing the MPD buffer. Profile is [%d].", profile);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHDecoder::CreateRepDecoders()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE(mMPDManager, NS_ERROR_NULL_POINTER);
|
||||
|
||||
// Global settings for the presentation.
|
||||
int64_t startTime = mMPDManager->GetStartTime();
|
||||
mDuration = mMPDManager->GetDuration();
|
||||
NS_ENSURE_TRUE(startTime >= 0 && mDuration > 0, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// For each audio/video stream, create a |ChannelMediaResource| object.
|
||||
|
||||
for (int i = 0; i < mMPDManager->GetNumAdaptationSets(); i++) {
|
||||
IMPDManager::AdaptationSetType asType = mMPDManager->GetAdaptationSetType(i);
|
||||
for (int j = 0; j < mMPDManager->GetNumRepresentations(i); j++) {
|
||||
// Get URL string.
|
||||
nsAutoString segmentUrl;
|
||||
nsresult rv = mMPDManager->GetFirstSegmentUrl(i, j, segmentUrl);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Get segment |nsIURI|; use MPD's base URI in case of relative paths.
|
||||
nsCOMPtr<nsIURI> url;
|
||||
rv = NS_NewURI(getter_AddRefs(url), segmentUrl, nullptr, mResource->URI());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoCString newUrl;
|
||||
rv = url->GetSpec(newUrl);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
LOG("Using URL=\"%s\" for AdaptationSet [%d] Representation [%d]",
|
||||
newUrl.get(), i, j);
|
||||
#endif
|
||||
|
||||
// 'file://' URLs are not supported.
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url);
|
||||
NS_ENSURE_FALSE(fileURL, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// Create |nsDASHRepDecoder| objects for each representation.
|
||||
if (asType == IMPDManager::DASH_VIDEO_STREAM) {
|
||||
Representation const * rep = mMPDManager->GetRepresentation(i, j);
|
||||
NS_ENSURE_TRUE(rep, NS_ERROR_NULL_POINTER);
|
||||
rv = CreateVideoRepDecoder(url, rep);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (asType == IMPDManager::DASH_AUDIO_STREAM) {
|
||||
Representation const * rep = mMPDManager->GetRepresentation(i, j);
|
||||
NS_ENSURE_TRUE(rep, NS_ERROR_NULL_POINTER);
|
||||
rv = CreateAudioRepDecoder(url, rep);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(mVideoRepDecoder, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_TRUE(mAudioRepDecoder, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHDecoder::CreateAudioRepDecoder(nsIURI* aUrl,
|
||||
mozilla::net::Representation const * aRep)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_ARG(aUrl);
|
||||
NS_ENSURE_ARG(aRep);
|
||||
NS_ENSURE_TRUE(mElement, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Create subdecoder and init with media element.
|
||||
nsDASHRepDecoder* audioDecoder = new nsDASHRepDecoder(this);
|
||||
NS_ENSURE_TRUE(audioDecoder->Init(mElement), NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (!mAudioRepDecoder) {
|
||||
mAudioRepDecoder = audioDecoder;
|
||||
}
|
||||
mAudioRepDecoders.AppendElement(audioDecoder);
|
||||
|
||||
// Create sub-reader; attach to DASH reader and sub-decoder.
|
||||
nsWebMReader* audioReader = new nsWebMReader(audioDecoder);
|
||||
if (mDASHReader) {
|
||||
mDASHReader->AddAudioReader(audioReader);
|
||||
}
|
||||
audioDecoder->SetReader(audioReader);
|
||||
|
||||
// Create media resource with URL and connect to sub-decoder.
|
||||
MediaResource* audioResource
|
||||
= CreateAudioSubResource(aUrl, static_cast<nsMediaDecoder*>(audioDecoder));
|
||||
NS_ENSURE_TRUE(audioResource, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
audioDecoder->SetResource(audioResource);
|
||||
audioDecoder->SetMPDRepresentation(aRep);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHDecoder::CreateVideoRepDecoder(nsIURI* aUrl,
|
||||
mozilla::net::Representation const * aRep)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_ARG(aUrl);
|
||||
NS_ENSURE_ARG(aRep);
|
||||
NS_ENSURE_TRUE(mElement, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Create subdecoder and init with media element.
|
||||
nsDASHRepDecoder* videoDecoder = new nsDASHRepDecoder(this);
|
||||
NS_ENSURE_TRUE(videoDecoder->Init(mElement), NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (!mVideoRepDecoder) {
|
||||
mVideoRepDecoder = videoDecoder;
|
||||
}
|
||||
mVideoRepDecoders.AppendElement(videoDecoder);
|
||||
|
||||
// Create sub-reader; attach to DASH reader and sub-decoder.
|
||||
nsWebMReader* videoReader = new nsWebMReader(videoDecoder);
|
||||
if (mDASHReader) {
|
||||
mDASHReader->AddVideoReader(videoReader);
|
||||
}
|
||||
videoDecoder->SetReader(videoReader);
|
||||
|
||||
// Create media resource with URL and connect to sub-decoder.
|
||||
MediaResource* videoResource
|
||||
= CreateVideoSubResource(aUrl, static_cast<nsMediaDecoder*>(videoDecoder));
|
||||
NS_ENSURE_TRUE(videoResource, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
videoDecoder->SetResource(videoResource);
|
||||
videoDecoder->SetMPDRepresentation(aRep);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozilla::MediaResource*
|
||||
nsDASHDecoder::CreateAudioSubResource(nsIURI* aUrl,
|
||||
nsMediaDecoder* aAudioDecoder)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE(aUrl, nullptr);
|
||||
NS_ENSURE_TRUE(aAudioDecoder, nullptr);
|
||||
|
||||
// Create channel for representation.
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsresult rv = CreateSubChannel(aUrl, getter_AddRefs(channel));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
// Create resource for representation.
|
||||
MediaResource* audioResource
|
||||
= MediaResource::Create(aAudioDecoder, channel);
|
||||
NS_ENSURE_TRUE(audioResource, nullptr);
|
||||
|
||||
return audioResource;
|
||||
}
|
||||
|
||||
mozilla::MediaResource*
|
||||
nsDASHDecoder::CreateVideoSubResource(nsIURI* aUrl,
|
||||
nsMediaDecoder* aVideoDecoder)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE(aUrl, nullptr);
|
||||
NS_ENSURE_TRUE(aVideoDecoder, nullptr);
|
||||
|
||||
// Create channel for representation.
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsresult rv = CreateSubChannel(aUrl, getter_AddRefs(channel));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
// Create resource for representation.
|
||||
MediaResource* videoResource
|
||||
= MediaResource::Create(aVideoDecoder, channel);
|
||||
NS_ENSURE_TRUE(videoResource, nullptr);
|
||||
|
||||
return videoResource;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHDecoder::CreateSubChannel(nsIURI* aUrl, nsIChannel** aChannel)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_ARG(aUrl);
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = mElement->GetDocumentLoadGroup();
|
||||
NS_ENSURE_TRUE(loadGroup, NS_ERROR_NULL_POINTER);
|
||||
|
||||
// Check for a Content Security Policy to pass down to the channel
|
||||
// created to load the media content.
|
||||
nsCOMPtr<nsIChannelPolicy> channelPolicy;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
nsresult rv = mElement->NodePrincipal()->GetCsp(getter_AddRefs(csp));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
if (csp) {
|
||||
channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
|
||||
channelPolicy->SetContentSecurityPolicy(csp);
|
||||
channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
|
||||
}
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
||||
aUrl,
|
||||
nullptr,
|
||||
loadGroup,
|
||||
nullptr,
|
||||
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY,
|
||||
channelPolicy);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
NS_ENSURE_TRUE(channel, NS_ERROR_NULL_POINTER);
|
||||
|
||||
NS_ADDREF(*aChannel = channel);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHDecoder::LoadRepresentations()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
nsresult rv;
|
||||
{
|
||||
// Hold the lock while we do this to set proper lock ordering
|
||||
// expectations for dynamic deadlock detectors: decoder lock(s)
|
||||
// should be grabbed before the cache lock.
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
|
||||
// Load the decoders for each |Representation|'s media streams.
|
||||
if (mAudioRepDecoder) {
|
||||
rv = mAudioRepDecoder->Load();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
if (mVideoRepDecoder) {
|
||||
rv = mVideoRepDecoder->Load();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Failed to open stream! rv [%x].", rv);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (mAudioRepDecoder) {
|
||||
mAudioRepDecoder->SetStateMachine(mDecoderStateMachine);
|
||||
}
|
||||
if (mVideoRepDecoder) {
|
||||
mVideoRepDecoder->SetStateMachine(mDecoderStateMachine);
|
||||
}
|
||||
|
||||
// Now that subreaders are init'd, it's ok to init state machine.
|
||||
return InitializeStateMachine(nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHDecoder::NotifyDownloadEnded(nsDASHRepDecoder* aRepDecoder,
|
||||
nsresult aStatus,
|
||||
MediaByteRange &aRange)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
// MPD Manager must exist, indicating MPD has been downloaded and parsed.
|
||||
if (!mMPDManager) {
|
||||
LOG1("Network Error! MPD Manager must exist, indicating MPD has been "
|
||||
"downloaded and parsed");
|
||||
NetworkError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Decoder for the media |Representation| must not be null.
|
||||
if (!aRepDecoder) {
|
||||
LOG1("Decoder for Representation is reported as null.");
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
// Return error if |aRepDecoder| does not match current audio/video decoder.
|
||||
if (aRepDecoder != mAudioRepDecoder && aRepDecoder != mVideoRepDecoder) {
|
||||
LOG("Error! Decoder [%p] does not match current sub-decoders!",
|
||||
aRepDecoder);
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
LOG("Byte range downloaded: decoder [%p] range requested [%d - %d]",
|
||||
aRepDecoder, aRange.mStart, aRange.mEnd);
|
||||
|
||||
// XXX Do Stream Switching here before loading next bytes, e.g.
|
||||
// decoder = PossiblySwitchDecoder(aRepDecoder);
|
||||
// decoder->LoadNextByteRange();
|
||||
aRepDecoder->LoadNextByteRange();
|
||||
return;
|
||||
} else if (aStatus == NS_BINDING_ABORTED) {
|
||||
LOG("MPD download has been cancelled by the user: aStatus [%x].", aStatus);
|
||||
if (mElement) {
|
||||
mElement->LoadAborted();
|
||||
}
|
||||
return;
|
||||
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
|
||||
LOG("Network error trying to download MPD: aStatus [%x].", aStatus);
|
||||
NetworkError();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHDecoder::LoadAborted()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
if (!mNotifiedLoadAborted && mElement) {
|
||||
mElement->LoadAborted();
|
||||
mNotifiedLoadAborted = true;
|
||||
LOG1("Load Aborted! Notifying media element.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHDecoder::Shutdown()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
// Notify reader of shutdown first.
|
||||
if (mDASHReader) {
|
||||
mDASHReader->NotifyDecoderShuttingDown();
|
||||
}
|
||||
|
||||
// Call parent class shutdown.
|
||||
nsBuiltinDecoder::Shutdown();
|
||||
NS_ENSURE_TRUE(mShuttingDown, );
|
||||
|
||||
// Shutdown reader thread if not already done.
|
||||
if (mMPDReaderThread) {
|
||||
nsresult rv = mMPDReaderThread->Shutdown();
|
||||
NS_ENSURE_SUCCESS(rv, );
|
||||
mMPDReaderThread = nullptr;
|
||||
}
|
||||
|
||||
// Forward to sub-decoders.
|
||||
for (uint i = 0; i < mAudioRepDecoders.Length(); i++) {
|
||||
if (mAudioRepDecoders[i]) {
|
||||
mAudioRepDecoders[i]->Shutdown();
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < mVideoRepDecoders.Length(); i++) {
|
||||
if (mVideoRepDecoders[i]) {
|
||||
mVideoRepDecoders[i]->Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHDecoder::DecodeError()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
nsBuiltinDecoder::DecodeError();
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &nsBuiltinDecoder::DecodeError);
|
||||
nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error dispatching DecodeError event to main thread: rv[%x]", rv);
|
||||
}
|
||||
}
|
||||
}
|
156
content/media/dash/nsDASHDecoder.h
Normal file
156
content/media/dash/nsDASHDecoder.h
Normal file
@ -0,0 +1,156 @@
|
||||
/* -*- 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 nsDASHDecoder.cpp for info on DASH interaction with the media engine.*/
|
||||
|
||||
#if !defined(nsDASHDecoder_h_)
|
||||
#define nsDASHDecoder_h_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsBuiltinDecoder.h"
|
||||
#include "nsDASHReader.h"
|
||||
|
||||
class nsDASHRepDecoder;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
class IMPDManager;
|
||||
class nsDASHMPDParser;
|
||||
class Representation;
|
||||
}// net
|
||||
}// mozilla
|
||||
|
||||
class nsDASHDecoder : public nsBuiltinDecoder
|
||||
{
|
||||
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;
|
||||
|
||||
nsDASHDecoder();
|
||||
~nsDASHDecoder();
|
||||
|
||||
// Clone not supported; just return nullptr.
|
||||
nsMediaDecoder* Clone() { return nullptr; }
|
||||
|
||||
// Creates a single state machine for all stream decoders.
|
||||
// Called from Load on the main thread only.
|
||||
nsDecoderStateMachine* CreateStateMachine();
|
||||
|
||||
// Loads the MPD from the network and subsequently loads the media streams.
|
||||
// Called from the main thread only.
|
||||
nsresult Load(MediaResource* aResource,
|
||||
nsIStreamListener** aListener,
|
||||
nsMediaDecoder* aCloneDonor);
|
||||
|
||||
// Notifies download of MPD file has ended.
|
||||
// Called on the main thread only.
|
||||
void NotifyDownloadEnded(nsresult aStatus);
|
||||
|
||||
// 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(nsDASHRepDecoder* aRepDecoder,
|
||||
nsresult aStatus,
|
||||
MediaByteRange &aRange);
|
||||
|
||||
// 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 |nsBuiltinDecoder|::|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);
|
||||
|
||||
// Creates audio/video resources for individual |Representation|s.
|
||||
// On the main thread.
|
||||
MediaResource* CreateAudioSubResource(nsIURI* aUrl,
|
||||
nsMediaDecoder* aAudioDecoder);
|
||||
MediaResource* CreateVideoSubResource(nsIURI* aUrl,
|
||||
nsMediaDecoder* 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.
|
||||
nsDASHReader* mDASHReader;
|
||||
|
||||
// Sub-decoder for current audio |Representation|.
|
||||
nsRefPtr<nsDASHRepDecoder> mAudioRepDecoder;
|
||||
// Array of pointers for the |Representation|s in the audio |AdaptationSet|.
|
||||
nsTArray<nsRefPtr<nsDASHRepDecoder> > mAudioRepDecoders;
|
||||
|
||||
// Sub-decoder for current video |Representation|.
|
||||
nsRefPtr<nsDASHRepDecoder> mVideoRepDecoder;
|
||||
// Array of pointers for the |Representation|s in the video |AdaptationSet|.
|
||||
nsTArray<nsRefPtr<nsDASHRepDecoder> > mVideoRepDecoders;
|
||||
};
|
||||
|
||||
#endif
|
329
content/media/dash/nsDASHReader.cpp
Normal file
329
content/media/dash/nsDASHReader.cpp
Normal file
@ -0,0 +1,329 @@
|
||||
/* -*- 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 nsDASHDecoder.cpp for info on DASH interaction with the media engine.*/
|
||||
|
||||
#include "nsTimeRanges.h"
|
||||
#include "VideoFrameContainer.h"
|
||||
#include "nsBuiltinDecoder.h"
|
||||
#include "nsDASHReader.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gBuiltinDecoderLog;
|
||||
#define LOG(msg, ...) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHReader] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHReader] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
nsDASHReader::Init(nsBuiltinDecoderReader* 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
|
||||
nsDASHReader::AddAudioReader(nsBuiltinDecoderReader* aAudioReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE(aAudioReader, );
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
mAudioReaders.AppendElement(aAudioReader);
|
||||
// XXX For now, just pick the first reader to be default.
|
||||
if (!mAudioReader)
|
||||
mAudioReader = aAudioReader;
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHReader::AddVideoReader(nsBuiltinDecoderReader* aVideoReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE(aVideoReader, );
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
mVideoReaders.AppendElement(aVideoReader);
|
||||
// XXX For now, just pick the first reader to be default.
|
||||
if (!mVideoReader)
|
||||
mVideoReader = aVideoReader;
|
||||
}
|
||||
|
||||
int64_t
|
||||
nsDASHReader::VideoQueueMemoryInUse()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
return (mVideoReader ? mVideoReader->VideoQueueMemoryInUse() : 0);
|
||||
}
|
||||
|
||||
int64_t
|
||||
nsDASHReader::AudioQueueMemoryInUse()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
return (mAudioReader ? mAudioReader->AudioQueueMemoryInUse() : 0);
|
||||
}
|
||||
|
||||
bool
|
||||
nsDASHReader::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
|
||||
nsDASHReader::DecodeAudioData()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
return (mAudioReader ? mAudioReader->DecodeAudioData() : false);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHReader::ReadMetadata(nsVideoInfo* aInfo,
|
||||
nsHTMLMediaElement::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);
|
||||
|
||||
// Get metadata from child readers.
|
||||
nsVideoInfo audioInfo, videoInfo;
|
||||
|
||||
if (mVideoReader) {
|
||||
rv = mVideoReader->ReadMetadata(&videoInfo, aTags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mInfo.mHasVideo = videoInfo.mHasVideo;
|
||||
mInfo.mDisplay = videoInfo.mDisplay;
|
||||
}
|
||||
if (mAudioReader) {
|
||||
rv = mAudioReader->ReadMetadata(&audioInfo, aTags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mInfo.mHasAudio = audioInfo.mHasAudio;
|
||||
mInfo.mAudioRate = audioInfo.mAudioRate;
|
||||
mInfo.mAudioChannels = audioInfo.mAudioChannels;
|
||||
mInfo.mStereoMode = audioInfo.mStereoMode;
|
||||
}
|
||||
|
||||
*aInfo = mInfo;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHReader::Seek(int64_t aTime,
|
||||
int64_t aStartTime,
|
||||
int64_t aEndTime,
|
||||
int64_t aCurrentTime)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (mAudioReader) {
|
||||
rv = mAudioReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
if (mVideoReader) {
|
||||
rv = mVideoReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHReader::GetBuffered(nsTimeRanges* aBuffered,
|
||||
int64_t aStartTime)
|
||||
{
|
||||
NS_ENSURE_ARG(aBuffered);
|
||||
|
||||
MediaResource* resource = nullptr;
|
||||
nsBuiltinDecoder* decoder = nullptr;
|
||||
|
||||
// Need to find intersect of |nsTimeRanges| for audio and video.
|
||||
nsTimeRanges audioBuffered, videoBuffered;
|
||||
uint32_t audioRangeCount, videoRangeCount;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// First, get buffered ranges for sub-readers.
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
if (mAudioReader) {
|
||||
decoder = mAudioReader->GetDecoder();
|
||||
NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
|
||||
resource = decoder->GetResource();
|
||||
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
|
||||
resource->Pin();
|
||||
rv = mAudioReader->GetBuffered(&audioBuffered, aStartTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
resource->Unpin();
|
||||
rv = audioBuffered.GetLength(&audioRangeCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
if (mVideoReader) {
|
||||
decoder = mVideoReader->GetDecoder();
|
||||
NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
|
||||
resource = decoder->GetResource();
|
||||
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
|
||||
resource->Pin();
|
||||
rv = mVideoReader->GetBuffered(&videoBuffered, aStartTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
resource->Unpin();
|
||||
rv = videoBuffered.GetLength(&videoRangeCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Now determine buffered data for available sub-readers.
|
||||
if (mAudioReader && mVideoReader) {
|
||||
// Calculate intersecting ranges.
|
||||
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 (mAudioReader) {
|
||||
*aBuffered = audioBuffered;
|
||||
} else if (mVideoReader) {
|
||||
*aBuffered = videoBuffered;
|
||||
} else {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
VideoData*
|
||||
nsDASHReader::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->DecodeToFirstData(&nsBuiltinDecoderReader::DecodeVideoFrame,
|
||||
VideoQueue());
|
||||
if (videoData) {
|
||||
videoStartTime = videoData->mTime;
|
||||
}
|
||||
}
|
||||
if (HasAudio()) {
|
||||
// Forward to audio reader.
|
||||
AudioData* audioData
|
||||
= mAudioReader->DecodeToFirstData(&nsBuiltinDecoderReader::DecodeAudioData,
|
||||
AudioQueue());
|
||||
if (audioData) {
|
||||
audioStartTime = audioData->mTime;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t startTime = NS_MIN(videoStartTime, audioStartTime);
|
||||
if (startTime != INT64_MAX) {
|
||||
aOutStartTime = startTime;
|
||||
}
|
||||
|
||||
return videoData;
|
||||
}
|
||||
|
||||
MediaQueue<AudioData>&
|
||||
nsDASHReader::AudioQueue()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(mAudioReader, "mAudioReader is NULL!");
|
||||
return mAudioReader->AudioQueue();
|
||||
}
|
||||
|
||||
MediaQueue<VideoData>&
|
||||
nsDASHReader::VideoQueue()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(mVideoReader, "mVideoReader is NULL!");
|
||||
return mVideoReader->VideoQueue();
|
||||
}
|
||||
|
||||
bool
|
||||
nsDASHReader::IsSeekableInBufferedRanges()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
// At least one subreader must exist, and all subreaders must return true.
|
||||
return (mVideoReader || mAudioReader) &&
|
||||
!((mVideoReader && !mVideoReader->IsSeekableInBufferedRanges()) ||
|
||||
(mAudioReader && !mAudioReader->IsSeekableInBufferedRanges()));
|
||||
}
|
314
content/media/dash/nsDASHReader.h
Normal file
314
content/media/dash/nsDASHReader.h
Normal file
@ -0,0 +1,314 @@
|
||||
/* -*- 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 nsDASHDecoder.cpp for comments on DASH object interaction
|
||||
*/
|
||||
|
||||
#if !defined(nsDASHReader_h_)
|
||||
#define nsDASHReader_h_
|
||||
|
||||
#include "nsBuiltinDecoderReader.h"
|
||||
|
||||
class nsDASHReader : public nsBuiltinDecoderReader
|
||||
{
|
||||
public:
|
||||
typedef mozilla::MediaResource MediaResource;
|
||||
|
||||
nsDASHReader(nsBuiltinDecoder* aDecoder) :
|
||||
nsBuiltinDecoderReader(aDecoder),
|
||||
mReadMetadataMonitor("media.dashreader.readmetadata"),
|
||||
mReadyToReadMetadata(false),
|
||||
mDecoderIsShuttingDown(false),
|
||||
mAudioReader(this),
|
||||
mVideoReader(this),
|
||||
mAudioReaders(this),
|
||||
mVideoReaders(this)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDASHReader);
|
||||
}
|
||||
~nsDASHReader()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsDASHReader);
|
||||
}
|
||||
|
||||
// Adds a pointer to a audio/video reader for a media |Representation|.
|
||||
// Called on the main thread only.
|
||||
void AddAudioReader(nsBuiltinDecoderReader* aAudioReader);
|
||||
void AddVideoReader(nsBuiltinDecoderReader* aVideoReader);
|
||||
|
||||
// Waits for metadata bytes to be downloaded, then reads and parses them.
|
||||
// Called on the decode thread only.
|
||||
nsresult ReadMetadata(nsVideoInfo* aInfo,
|
||||
nsHTMLMediaElement::MetadataTags** aTags);
|
||||
|
||||
// Waits for |ReadyToReadMetadata| or |NotifyDecoderShuttingDown|
|
||||
// notification, whichever comes first. Ensures no attempt to read metadata
|
||||
// during |nsDASHDecoder|::|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 |nsDASHDecoder| 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 |nsDASHDecoder| 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() {
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
return mAudioReader ? mAudioReader->HasAudio() : false;
|
||||
}
|
||||
bool HasVideo() {
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
return mVideoReader ? mVideoReader->HasVideo() : false;
|
||||
}
|
||||
|
||||
// Returns references to the audio/video queues of sub-readers. Called on
|
||||
// decode, state machine and audio threads.
|
||||
MediaQueue<AudioData>& AudioQueue();
|
||||
MediaQueue<VideoData>& VideoQueue();
|
||||
|
||||
// Called from nsBuiltinDecoderStateMachine on the main thread.
|
||||
nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
|
||||
|
||||
// Used by |MediaMemoryReporter|.
|
||||
int64_t VideoQueueMemoryInUse();
|
||||
int64_t AudioQueueMemoryInUse();
|
||||
|
||||
// Called on the decode thread.
|
||||
bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold);
|
||||
bool DecodeAudioData();
|
||||
|
||||
// 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);
|
||||
|
||||
// Called by state machine on multiple threads.
|
||||
nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
|
||||
|
||||
// Called on the state machine or decode threads.
|
||||
VideoData* FindStartTime(int64_t& aOutStartTime);
|
||||
|
||||
// Call by state machine on multiple threads.
|
||||
bool IsSeekableInBufferedRanges();
|
||||
|
||||
private:
|
||||
// Similar to |ReentrantMonitorAutoEnter|, this class enters the supplied
|
||||
// monitor in its constructor, but only if the conditional value |aEnter| is
|
||||
// true. Used here to allow read access on the sub-readers' owning thread,
|
||||
// i.e. the decode thread, while locking write accesses from all threads,
|
||||
// and read accesses from non-decode threads.
|
||||
class ReentrantMonitorConditionallyEnter
|
||||
{
|
||||
public:
|
||||
ReentrantMonitorConditionallyEnter(bool aEnter,
|
||||
ReentrantMonitor &aReentrantMonitor) :
|
||||
mReentrantMonitor(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDASHReader::ReentrantMonitorConditionallyEnter);
|
||||
if (aEnter) {
|
||||
mReentrantMonitor = &aReentrantMonitor;
|
||||
NS_ASSERTION(mReentrantMonitor, "null monitor");
|
||||
mReentrantMonitor->Enter();
|
||||
}
|
||||
}
|
||||
~ReentrantMonitorConditionallyEnter(void)
|
||||
{
|
||||
if (mReentrantMonitor) {
|
||||
mReentrantMonitor->Exit();
|
||||
}
|
||||
MOZ_COUNT_DTOR(nsDASHReader::ReentrantMonitorConditionallyEnter);
|
||||
}
|
||||
private:
|
||||
// Restrict to constructor and destructor defined above.
|
||||
ReentrantMonitorConditionallyEnter();
|
||||
ReentrantMonitorConditionallyEnter(const ReentrantMonitorConditionallyEnter&);
|
||||
ReentrantMonitorConditionallyEnter& operator =(const ReentrantMonitorConditionallyEnter&);
|
||||
static void* operator new(size_t) CPP_THROW_NEW;
|
||||
static void operator delete(void*);
|
||||
|
||||
// Ptr to the |ReentrantMonitor| object. Null if |aEnter| in constructor
|
||||
// was false.
|
||||
ReentrantMonitor* mReentrantMonitor;
|
||||
};
|
||||
|
||||
// Monitor and booleans used to wait for metadata bytes to be downloaded, and
|
||||
// skip reading metadata if |nsDASHDecoder|'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 |nsDASHReader| to verify
|
||||
// correct entry into the decoder's |ReentrantMonitor|.
|
||||
MonitoredSubReader(nsDASHReader* aReader) :
|
||||
mReader(aReader),
|
||||
mSubReader(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDASHReader::MonitoredSubReader);
|
||||
NS_ASSERTION(mReader, "Reader is null!");
|
||||
}
|
||||
// Note: |mSubReader|'s refcount will be decremented in this destructor.
|
||||
~MonitoredSubReader()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsDASHReader::MonitoredSubReader);
|
||||
}
|
||||
|
||||
// Override '=' to always assert thread is "in monitor" for writes/changes
|
||||
// to |mSubReader|.
|
||||
MonitoredSubReader& operator=(nsBuiltinDecoderReader* 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 nsBuiltinDecoderReader*() 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.
|
||||
nsBuiltinDecoderReader* operator->() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
// Pointer to |nsDASHReader| object which owns this |MonitoredSubReader|.
|
||||
nsDASHReader* mReader;
|
||||
// Ref ptr to the sub reader.
|
||||
nsRefPtr<nsBuiltinDecoderReader> 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 |nsDASHReader| to verify
|
||||
// correct entry into the decoder's |ReentrantMonitor|.
|
||||
MonitoredSubReaderList(nsDASHReader* aReader) :
|
||||
mReader(aReader)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDASHReader::MonitoredSubReaderList);
|
||||
NS_ASSERTION(mReader, "Reader is null!");
|
||||
}
|
||||
// Note: Elements in |mSubReaderList| will have their refcounts decremented
|
||||
// in this destructor.
|
||||
~MonitoredSubReaderList()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsDASHReader::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();
|
||||
}
|
||||
|
||||
// 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<nsBuiltinDecoderReader>& 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(nsBuiltinDecoderReader* aReader)
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
mSubReaderList.AppendElement(aReader);
|
||||
}
|
||||
private:
|
||||
// Pointer to |nsDASHReader| object which owns this |MonitoredSubReader|.
|
||||
nsDASHReader* mReader;
|
||||
// Ref ptrs to the sub readers.
|
||||
nsTArray<nsRefPtr<nsBuiltinDecoderReader> > 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;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
389
content/media/dash/nsDASHRepDecoder.cpp
Normal file
389
content/media/dash/nsDASHRepDecoder.cpp
Normal file
@ -0,0 +1,389 @@
|
||||
/* -*- 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 nsDASHDecoder.cpp for info on DASH interaction with the media engine.*/
|
||||
|
||||
#include "prlog.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "SegmentBase.h"
|
||||
#include "nsBuiltinDecoderStateMachine.h"
|
||||
#include "nsDASHReader.h"
|
||||
#include "MediaResource.h"
|
||||
#include "nsDASHRepDecoder.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gBuiltinDecoderLog;
|
||||
#define LOG(msg, ...) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHRepDecoder] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHRepDecoder] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
nsDecoderStateMachine*
|
||||
nsDASHRepDecoder::CreateStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
// Do not create; just return current state machine.
|
||||
return mDecoderStateMachine;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHRepDecoder::SetStateMachine(nsDecoderStateMachine* aSM)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mDecoderStateMachine = aSM;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::SetResource(MediaResource* aResource)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mResource = aResource;
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::SetMPDRepresentation(Representation const * aRep)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mMPDRepresentation = aRep;
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::SetReader(nsWebMReader* aReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mReader = aReader;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHRepDecoder::Load(MediaResource* aResource,
|
||||
nsIStreamListener** aListener,
|
||||
nsMediaDecoder* 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 |nsMediaCache|'s control of
|
||||
// seeking (see |nsMediaCache|::|Update|). |nsMediaCache| 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 |nsMediaCache| can assume the bytes are en route.
|
||||
int64_t delta = NS_MAX(mIndexByteRange.mStart, mInitByteRange.mStart)
|
||||
- NS_MIN(mIndexByteRange.mEnd, mInitByteRange.mEnd);
|
||||
MediaByteRange byteRange;
|
||||
if (delta <= SEEK_VS_READ_THRESHOLD) {
|
||||
byteRange.mStart = NS_MIN(mIndexByteRange.mStart, mInitByteRange.mStart);
|
||||
byteRange.mEnd = NS_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
|
||||
nsDASHRepDecoder::NotifyDownloadEnded(nsresult aStatus)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
if (!mMainDecoder) {
|
||||
LOG("Error! Main Decoder is reported as null: mMainDecoder [%p]",
|
||||
mMainDecoder.get());
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
// 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 [%d - %d]",
|
||||
mMetadataChunkCount,
|
||||
mCurrentByteRange.mStart, mCurrentByteRange.mEnd);
|
||||
mMetadataChunkCount--;
|
||||
} else {
|
||||
// Notify main decoder that a DATA byte range is downloaded.
|
||||
LOG("Byte range downloaded: status [%x] range requested [%d - %d]",
|
||||
aStatus, mCurrentByteRange.mStart, mCurrentByteRange.mEnd);
|
||||
mMainDecoder->NotifyDownloadEnded(this, aStatus,
|
||||
mCurrentByteRange);
|
||||
}
|
||||
} else if (aStatus == NS_BINDING_ABORTED) {
|
||||
LOG("MPD 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
|
||||
nsDASHRepDecoder::OnReadMetadataCompleted()
|
||||
{
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
LOG1("Metadata has been read.");
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &nsDASHRepDecoder::LoadNextByteRange);
|
||||
nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error dispatching parse event to main thread: rv[%x]", rv);
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::LoadNextByteRange()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (!mResource) {
|
||||
LOG1("Error: resource is reported as null!");
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate the array of subsegment byte ranges if it's empty.
|
||||
nsresult rv;
|
||||
if (mByteRanges.IsEmpty()) {
|
||||
if (!mReader) {
|
||||
LOG1("Error: mReader should not be null!");
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
rv = mReader->GetIndexByteRanges(mByteRanges);
|
||||
// If empty, just fail.
|
||||
if (NS_FAILED(rv) || mByteRanges.IsEmpty()) {
|
||||
LOG1("Error getting list of subsegment byte ranges.");
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get byte range for subsegment.
|
||||
if (mSubsegmentIdx < mByteRanges.Length()) {
|
||||
mCurrentByteRange = mByteRanges[mSubsegmentIdx];
|
||||
} else {
|
||||
mCurrentByteRange.Clear();
|
||||
LOG("End of subsegments: index [%d] out of range.", mSubsegmentIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Open byte range corresponding to subsegment.
|
||||
rv = mResource->OpenByteRange(nullptr, mCurrentByteRange);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error opening byte range [%d - %d]: rv [%x].",
|
||||
mCurrentByteRange.mStart, mCurrentByteRange.mEnd, rv);
|
||||
NetworkError();
|
||||
return;
|
||||
}
|
||||
// Increment subsegment index for next load.
|
||||
mSubsegmentIdx++;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDASHRepDecoder::GetByteRangeForSeek(int64_t const aOffset,
|
||||
MediaByteRange& aByteRange)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
// Check data ranges, if available.
|
||||
for (int i = 0; i < mByteRanges.Length(); i++) {
|
||||
NS_ENSURE_FALSE(mByteRanges[i].IsNull(), NS_ERROR_NOT_INITIALIZED);
|
||||
if (mByteRanges[i].mStart <= aOffset && aOffset <= mByteRanges[i].mEnd) {
|
||||
mCurrentByteRange = aByteRange = mByteRanges[i];
|
||||
mSubsegmentIdx = i;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
// Check metadata ranges; init range.
|
||||
if (mInitByteRange.mStart <= aOffset && aOffset <= mInitByteRange.mEnd) {
|
||||
mCurrentByteRange = aByteRange = mInitByteRange;
|
||||
mSubsegmentIdx = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
// ... index range.
|
||||
if (mIndexByteRange.mStart <= aOffset && aOffset <= mIndexByteRange.mEnd) {
|
||||
mCurrentByteRange = aByteRange = mIndexByteRange;
|
||||
mSubsegmentIdx = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
aByteRange.Clear();
|
||||
if (mByteRanges.IsEmpty()) {
|
||||
// Assume mByteRanges will be populated after metadata is read.
|
||||
LOG("Can't get range for offset [%d].", 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 [%d] is in an unknown range!", aOffset);
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::NetworkError()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->NetworkError(); }
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::SetDuration(double aDuration)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->SetDuration(aDuration); }
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::SetInfinite(bool aInfinite)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->SetInfinite(aInfinite); }
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::SetSeekable(bool aSeekable)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->SetSeekable(aSeekable); }
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::Progress(bool aTimer)
|
||||
{
|
||||
if (mMainDecoder) { mMainDecoder->Progress(aTimer); }
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
LOG("Data bytes [%d - %d] arrived via buffer [%p].",
|
||||
aOffset, aOffset+aLength, aBuffer);
|
||||
// Notify reader directly, since call to |nsBuiltinDecoderStateMachine|::
|
||||
// |NotifyDataArrived| will go to |nsDASHReader|::|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
|
||||
nsDASHRepDecoder::NotifyBytesDownloaded()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->NotifyBytesDownloaded(); }
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::NotifySuspendedStatusChanged()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->NotifySuspendedStatusChanged(); }
|
||||
}
|
||||
|
||||
bool
|
||||
nsDASHRepDecoder::OnStateMachineThread() const
|
||||
{
|
||||
return (mMainDecoder ? mMainDecoder->OnStateMachineThread() : false);
|
||||
}
|
||||
|
||||
bool
|
||||
nsDASHRepDecoder::OnDecodeThread() const
|
||||
{
|
||||
return (mMainDecoder ? mMainDecoder->OnDecodeThread() : false);
|
||||
}
|
||||
|
||||
ReentrantMonitor&
|
||||
nsDASHRepDecoder::GetReentrantMonitor()
|
||||
{
|
||||
return mMainDecoder->GetReentrantMonitor();
|
||||
}
|
||||
|
||||
nsDecoderStateMachine::State
|
||||
nsDASHRepDecoder::GetDecodeState()
|
||||
{
|
||||
// XXX SHUTDOWN might not be an appropriate error.
|
||||
return (mMainDecoder ? mMainDecoder->GetDecodeState()
|
||||
: nsDecoderStateMachine::DECODER_STATE_SHUTDOWN);
|
||||
}
|
||||
|
||||
mozilla::layers::ImageContainer*
|
||||
nsDASHRepDecoder::GetImageContainer()
|
||||
{
|
||||
NS_ASSERTION(mMainDecoder && mMainDecoder->OnDecodeThread(),
|
||||
"Should be on decode thread.");
|
||||
return (mMainDecoder ? mMainDecoder->GetImageContainer() : nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
nsDASHRepDecoder::DecodeError()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
nsBuiltinDecoder::DecodeError();
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &nsBuiltinDecoder::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
|
||||
nsDASHRepDecoder::ReleaseStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
||||
|
||||
// Since state machine owns mReader, remove reference to it.
|
||||
mReader = nullptr;
|
||||
|
||||
nsBuiltinDecoder::ReleaseStateMachine();
|
||||
}
|
192
content/media/dash/nsDASHRepDecoder.h
Normal file
192
content/media/dash/nsDASHRepDecoder.h
Normal file
@ -0,0 +1,192 @@
|
||||
/* -*- 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 nsDASHDecoder.cpp for info on DASH interaction with the media engine.*/
|
||||
|
||||
#if !defined(nsDASHRepDecoder_h_)
|
||||
#define nsDASHRepDecoder_h_
|
||||
|
||||
#include "Representation.h"
|
||||
#include "ImageLayers.h"
|
||||
#include "nsDASHDecoder.h"
|
||||
#include "nsWebMDecoder.h"
|
||||
#include "nsWebMReader.h"
|
||||
#include "nsBuiltinDecoder.h"
|
||||
|
||||
class nsDASHDecoder;
|
||||
|
||||
class nsDASHRepDecoder : public nsBuiltinDecoder
|
||||
{
|
||||
public:
|
||||
typedef mozilla::net::Representation Representation;
|
||||
typedef mozilla::net::SegmentBase SegmentBase;
|
||||
typedef mozilla::layers::ImageContainer ImageContainer;
|
||||
|
||||
// Constructor takes a ptr to the main decoder.
|
||||
nsDASHRepDecoder(nsDASHDecoder* aMainDecoder) :
|
||||
mMainDecoder(aMainDecoder),
|
||||
mMPDRepresentation(nullptr),
|
||||
mMetadataChunkCount(0),
|
||||
mCurrentByteRange(),
|
||||
mSubsegmentIdx(0),
|
||||
mReader(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsDASHRepDecoder);
|
||||
}
|
||||
|
||||
~nsDASHRepDecoder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsDASHRepDecoder);
|
||||
}
|
||||
|
||||
// Clone not supported; just return nullptr.
|
||||
virtual nsMediaDecoder* 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(nsDecoderStateMachine* aSM);
|
||||
|
||||
private:
|
||||
// Overridden to return the ptr set by SetStateMachine. Called on the main
|
||||
// thread only.
|
||||
nsDecoderStateMachine* CreateStateMachine();
|
||||
|
||||
public:
|
||||
// Called by nsDASHDecoder 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 nsDASHDecoder on main thread; Starts media stream download.
|
||||
nsresult Load(MediaResource* aResource = nullptr,
|
||||
nsIStreamListener** aListener = nullptr,
|
||||
nsMediaDecoder* aCloneDonor = nullptr);
|
||||
|
||||
// Loads the next byte range (or first one on first call). Called on the main
|
||||
// thread only.
|
||||
void LoadNextByteRange();
|
||||
|
||||
// Calls from nsDASHRepDecoder. Called on the main thread only.
|
||||
void SetReader(nsWebMReader* aReader);
|
||||
|
||||
// Called if the media file encounters a network error. Call on the main
|
||||
// thread only.
|
||||
void NetworkError();
|
||||
|
||||
// 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 SetSeekable(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 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();
|
||||
|
||||
// Gets a byte range containing the byte offset. Call on main thread only.
|
||||
nsresult GetByteRangeForSeek(int64_t const aOffset,
|
||||
MediaByteRange& aByteRange);
|
||||
|
||||
// Returns true if the current thread is the state machine thread.
|
||||
bool OnStateMachineThread() const;
|
||||
|
||||
// Returns true if the current thread is the decode thread.
|
||||
bool OnDecodeThread() const;
|
||||
|
||||
// Returns main decoder's monitor for synchronised access.
|
||||
ReentrantMonitor& GetReentrantMonitor();
|
||||
|
||||
// Return the current decode state, according to the main decoder. The
|
||||
// decoder monitor must be obtained before calling this.
|
||||
nsDecoderStateMachine::State GetDecodeState();
|
||||
|
||||
// Called on the decode thread from nsWebMReader.
|
||||
ImageContainer* GetImageContainer();
|
||||
|
||||
// Called when Metadata has been read; notifies that index data is read.
|
||||
// Called on the decode thread only.
|
||||
void OnReadMetadataCompleted();
|
||||
|
||||
// Overridden to cleanup ref to |nsDASHDecoder|. Called on main thread only.
|
||||
void Shutdown() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
// Call parent class shutdown.
|
||||
nsBuiltinDecoder::Shutdown();
|
||||
NS_ENSURE_TRUE(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:
|
||||
// The main decoder.
|
||||
nsRefPtr<nsDASHDecoder> 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.
|
||||
uint64_t mSubsegmentIdx;
|
||||
|
||||
// Ptr to the reader object for this |Representation|. Owned by state
|
||||
// machine.
|
||||
nsBuiltinDecoderReader* mReader;
|
||||
};
|
||||
|
||||
#endif //nsDASHRepDecoder_h_
|
@ -711,7 +711,7 @@ public:
|
||||
virtual void NotifyAudioAvailableListener();
|
||||
|
||||
// Notifies the element that decoding has failed.
|
||||
void DecodeError();
|
||||
virtual void DecodeError();
|
||||
|
||||
// Schedules the state machine to run one cycle on the shared state
|
||||
// machine thread. Main thread only.
|
||||
|
@ -376,24 +376,6 @@ VideoData* nsBuiltinDecoderReader::FindStartTime(int64_t& aOutStartTime)
|
||||
return videoData;
|
||||
}
|
||||
|
||||
/*template<class Data>
|
||||
Data* nsBuiltinDecoderReader::DecodeToFirstData(DecodeFn aDecodeFn,
|
||||
MediaQueue<Data>& aQueue)
|
||||
{
|
||||
bool eof = false;
|
||||
while (!eof && aQueue.GetSize() == 0) {
|
||||
{
|
||||
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
|
||||
if (mDecoder->GetDecodeState() == nsDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
eof = !(this->*aDecodeFn)();
|
||||
}
|
||||
Data* d = nullptr;
|
||||
return (d = aQueue.PeekFront()) ? d : nullptr;
|
||||
}*/
|
||||
|
||||
nsresult nsBuiltinDecoderReader::DecodeToTarget(int64_t aTarget)
|
||||
{
|
||||
// Decode forward to the target frame. Start with video, if we have it.
|
||||
|
@ -530,7 +530,7 @@ public:
|
||||
virtual void SetIndexByteRange(MediaByteRange &aByteRange) { }
|
||||
|
||||
// Returns list of ranges for index frame start/end offsets. Used by DASH.
|
||||
nsresult GetIndexByteRanges(nsTArray<MediaByteRange>& aByteRanges) {
|
||||
virtual nsresult GetIndexByteRanges(nsTArray<MediaByteRange>& aByteRanges) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
|
@ -32,3 +32,9 @@ include $(topsrcdir)/config/rules.mk
|
||||
LOCAL_INCLUDES = \
|
||||
$(MOZ_LIBVPX_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_DASH
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(srcdir)/../dash \
|
||||
$(NULL)
|
||||
endif
|
||||
|
@ -191,6 +191,12 @@ SHARED_LIBRARY_LIBS += \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_DASH
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/content/media/dash/$(LIB_PREFIX)gkcondash_s.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
|
||||
INCLUDES += \
|
||||
-I$(srcdir)/../../base/src \
|
||||
|
@ -165,6 +165,9 @@ pref("media.wave.enabled", true);
|
||||
#ifdef MOZ_WEBM
|
||||
pref("media.webm.enabled", true);
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
pref("media.dash.enabled", true);
|
||||
#endif
|
||||
#ifdef MOZ_GSTREAMER
|
||||
pref("media.h264.enabled", true);
|
||||
#endif
|
||||
|
@ -8,14 +8,14 @@
|
||||
# Contributor(s):
|
||||
# Steve Workman <sworkman@mozilla.com
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
DEPTH := @DEPTH@
|
||||
topsrcdir := @top_srcdir@
|
||||
srcdir := @srcdir@
|
||||
VPATH := @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
PARALLEL_DIRS = \
|
||||
PARALLEL_DIRS := \
|
||||
mpd \
|
||||
$(NULL)
|
||||
|
||||
|
@ -6,23 +6,21 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Steve Workman <sworkman@mozilla.com
|
||||
# Steve Workman <sworkman@mozilla.com>
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
DEPTH := @DEPTH@
|
||||
topsrcdir := @top_srcdir@
|
||||
srcdir := @srcdir@
|
||||
VPATH := @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = necko
|
||||
LIBRARY_NAME = nkdashmpd_s
|
||||
LIBXUL_LIBRARY = 1
|
||||
XPIDL_MODULE = necko_dashmpd
|
||||
GRE_MODULE = 1
|
||||
FORCE_STATIC_LIB = 1
|
||||
MODULE := necko
|
||||
LIBRARY_NAME := nkdashmpd_s
|
||||
LIBXUL_LIBRARY := 1
|
||||
FORCE_STATIC_LIB := 1
|
||||
|
||||
CPPSRCS = \
|
||||
CPPSRCS := \
|
||||
nsDASHMPDParser.cpp \
|
||||
IMPDManager.cpp \
|
||||
nsDASHWebMODManager.cpp \
|
||||
@ -34,22 +32,13 @@ CPPSRCS = \
|
||||
SegmentBase.cpp \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS = \
|
||||
IMPDManager.h \
|
||||
nsDASHMPDParser.h \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/../manager/ \
|
||||
LOCAL_INCLUDES := \
|
||||
-I$(topsrcdir)/content/base/src \
|
||||
-I$(topsrcdir)/content/html/content/public \
|
||||
-I$(topsrcdir)/content/html/content/src \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
DEFINES += -DIMPL_NS_NET
|
||||
|
||||
|
||||
|
@ -38,9 +38,12 @@
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo* gDASHMPDParserLog = nullptr;
|
||||
#define LOG(msg, ...) PR_LOG(gDASHMPDParserLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHMPDParser] " msg, this, ##__VA_ARGS__))
|
||||
("%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 {
|
||||
@ -83,7 +86,7 @@ nsDASHMPDParser::Parse(IMPDManager** aMPDManager,
|
||||
nsAutoCString spec;
|
||||
nsresult rv = mURI->GetSpec(spec);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Preparing to parse MPD: cannot get spec from URI");
|
||||
LOG1("Preparing to parse MPD: cannot get spec from URI");
|
||||
} else {
|
||||
LOG("Preparing to parse MPD: mURI:\"%s\"", spec.get());
|
||||
}
|
||||
@ -103,7 +106,7 @@ nsDASHMPDParser::Parse(IMPDManager** aMPDManager,
|
||||
getter_AddRefs(doc));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if(!doc) {
|
||||
LOG("ERROR! Document not parsed as XML!");
|
||||
LOG1("ERROR! Document not parsed as XML!");
|
||||
return NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
// Use root node to create MPD manager.
|
||||
|
@ -46,9 +46,12 @@ namespace net {
|
||||
static PRLogModuleInfo* gnsDASHWebMODManagerLog = nullptr;
|
||||
#define LOG(msg, ...) \
|
||||
PR_LOG(gnsDASHWebMODManagerLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHWebMODManager] " msg, this, ##__VA_ARGS__))
|
||||
("%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)
|
||||
|
@ -43,9 +43,13 @@
|
||||
static PRLogModuleInfo* gnsDASHWebMODParserLog = nullptr;
|
||||
#define LOG(msg, ...) \
|
||||
PR_LOG(gnsDASHWebMODParserLog, PR_LOG_DEBUG, \
|
||||
("%p [nsDASHWebMODParser] " msg, this, ##__VA_ARGS__))
|
||||
("%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 {
|
||||
@ -59,7 +63,7 @@ nsDASHWebMODParser::nsDASHWebMODParser(nsIDOMElement* aRoot) :
|
||||
if(!gnsDASHWebMODParserLog)
|
||||
gnsDASHWebMODParserLog = PR_NewLogModule("nsDASHWebMODParser");
|
||||
#endif
|
||||
LOG("Created nsDASHWebMODParser");
|
||||
LOG1("Created nsDASHWebMODParser");
|
||||
}
|
||||
|
||||
nsDASHWebMODParser::~nsDASHWebMODParser()
|
||||
@ -70,7 +74,7 @@ nsDASHWebMODParser::~nsDASHWebMODParser()
|
||||
MPD*
|
||||
nsDASHWebMODParser::Parse()
|
||||
{
|
||||
LOG("Parsing DOM into MPD objects");
|
||||
LOG1("Parsing DOM into MPD objects");
|
||||
nsAutoPtr<MPD> mpd(new MPD());
|
||||
|
||||
nsresult rv = VerifyMPDAttributes();
|
||||
@ -219,7 +223,7 @@ nsDASHWebMODParser::SetPeriods(MPD* aMpd)
|
||||
|
||||
// |Period| should be ignored if its child elems are invalid
|
||||
if (bIgnoreThisPeriod) {
|
||||
LOG("Ignoring period");
|
||||
LOG1("Ignoring period");
|
||||
} else {
|
||||
aMpd->AddPeriod(period.forget());
|
||||
LOG("Period #%d: added to MPD", i++);
|
||||
@ -250,7 +254,7 @@ nsDASHWebMODParser::ValidateAdaptationSetAttributes(nsIDOMElement* aChild,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bAttributesValid = !mimeType.IsEmpty();
|
||||
if (!bAttributesValid)
|
||||
LOG("mimeType not present!");
|
||||
LOG1("mimeType not present!");
|
||||
}
|
||||
// Validate attributes for video.
|
||||
if (bAttributesValid && mimeType.EqualsLiteral(VIDEO_WEBM)) {
|
||||
@ -260,7 +264,7 @@ nsDASHWebMODParser::ValidateAdaptationSetAttributes(nsIDOMElement* aChild,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bAttributesValid = (value.IsEmpty() || value.EqualsLiteral("true"));
|
||||
if (!bAttributesValid)
|
||||
LOG("segmentAlignment not present or invalid!");
|
||||
LOG1("segmentAlignment not present or invalid!");
|
||||
}
|
||||
if (bAttributesValid) {
|
||||
rv = GetAttribute(aChild, NS_LITERAL_STRING("subsegmentAlignment"),
|
||||
@ -268,7 +272,7 @@ nsDASHWebMODParser::ValidateAdaptationSetAttributes(nsIDOMElement* aChild,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bAttributesValid = (!value.IsEmpty() && value.EqualsLiteral("true"));
|
||||
if (!bAttributesValid)
|
||||
LOG("subsegmentAlignment not present or invalid!");
|
||||
LOG1("subsegmentAlignment not present or invalid!");
|
||||
}
|
||||
if (bAttributesValid) {
|
||||
rv = GetAttribute(aChild, NS_LITERAL_STRING("bitstreamSwitching"),
|
||||
@ -276,7 +280,7 @@ nsDASHWebMODParser::ValidateAdaptationSetAttributes(nsIDOMElement* aChild,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bAttributesValid = (!value.IsEmpty() && value.EqualsLiteral("true"));
|
||||
if (!bAttributesValid)
|
||||
LOG("bitstreamSwitching not present or invalid!");
|
||||
LOG1("bitstreamSwitching not present or invalid!");
|
||||
}
|
||||
} else if (bAttributesValid && mimeType.EqualsLiteral(AUDIO_WEBM)) {
|
||||
// Validate attributes for audio.
|
||||
|
@ -136,6 +136,9 @@
|
||||
#define VIDEO_OGG "video/ogg"
|
||||
#define VIDEO_WEBM "video/webm"
|
||||
#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"
|
||||
|
@ -402,6 +402,9 @@ static nsDefaultMimeTypeEntry defaultMimeEntries [] =
|
||||
{ VIDEO_WEBM, "webm" },
|
||||
{ AUDIO_WEBM, "webm" },
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
{ APPLICATION_DASH, "mpd" },
|
||||
#endif
|
||||
#ifdef MOZ_GSTREAMER
|
||||
{ VIDEO_MP4, "mp4" },
|
||||
#endif
|
||||
@ -476,6 +479,9 @@ static nsExtraMimeTypeEntry extraMimeEntries [] =
|
||||
{ AUDIO_OGG, "opus", "Opus Audio" },
|
||||
{ VIDEO_WEBM, "webm", "Web Media Video" },
|
||||
{ AUDIO_WEBM, "webm", "Web Media Audio" },
|
||||
#ifdef MOZ_DASH
|
||||
{ APPLICATION_DASH, "mpd", "DASH Media Presentation Description" },
|
||||
#endif
|
||||
#ifdef MOZ_MEDIA_PLUGINS
|
||||
{ AUDIO_MP3, "mp3", "MPEG Audio" },
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user