mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge mozilla-central to fx-team
This commit is contained in:
commit
7a60858f4d
23
Makefile.in
23
Makefile.in
@ -17,13 +17,16 @@ export TOPLEVEL_BUILD := 1
|
||||
|
||||
default::
|
||||
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
include $(topsrcdir)/$(MOZ_BUILD_APP)/build.mk
|
||||
endif
|
||||
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
ifndef LIBXUL_SDK
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
BUILD_JS = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
GARBAGE_DIRS += dist _javagen _profile staticlib
|
||||
DIST_GARBAGE = config.cache config.log config.status* config-defs.h \
|
||||
config/autoconf.mk \
|
||||
@ -35,7 +38,7 @@ ifndef MOZ_PROFILE_USE
|
||||
# We need to explicitly put backend.RecursiveMakeBackend here
|
||||
# otherwise the rule in rules.mk doesn't run early enough.
|
||||
libs binaries export tools:: CLOBBER $(topsrcdir)/configure config.status backend.RecursiveMakeBackend
|
||||
ifndef LIBXUL_SDK
|
||||
ifdef BUILD_JS
|
||||
libs binaries export tools:: js-config-status
|
||||
endif
|
||||
endif
|
||||
@ -78,7 +81,7 @@ include backend.RecursiveMakeBackend.pp
|
||||
|
||||
default:: backend.RecursiveMakeBackend
|
||||
|
||||
ifndef LIBXUL_SDK
|
||||
ifdef BUILD_JS
|
||||
.PHONY: js-config-status
|
||||
js-config-status:
|
||||
$(call SUBMAKE,backend.RecursiveMakeBackend,js/src,1)
|
||||
@ -92,7 +95,7 @@ install_manifest_depends = \
|
||||
backend.RecursiveMakeBackend \
|
||||
$(NULL)
|
||||
|
||||
ifndef LIBXUL_SDK
|
||||
ifdef BUILD_JS
|
||||
install_manifest_depends += js-config-status
|
||||
endif
|
||||
|
||||
@ -101,12 +104,12 @@ install-manifests: $(addprefix install-dist-,$(install_manifests))
|
||||
|
||||
.PHONY: $(addprefix install-dist-,$(install_manifests))
|
||||
$(addprefix install-dist-,$(install_manifests)): install-dist-%: $(install_manifest_depends)
|
||||
$(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )$(DIST)/$* _build_manifests/install/dist_$* $(if $(LIBXUL_SDK),,js/src/_build_manifests/install/dist_$*))
|
||||
$(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )$(DIST)/$* _build_manifests/install/dist_$* $(if $(BUILD_JS),js/src/_build_manifests/install/dist_$*))
|
||||
|
||||
.PHONY: install-tests
|
||||
install-manifests: install-tests
|
||||
install-tests: $(install_manifest_depends)
|
||||
$(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )_tests _build_manifests/install/tests js/src/_build_manifests/install/tests)
|
||||
$(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )_tests _build_manifests/install/tests $(if $(BUILD_JS),js/src/_build_manifests/install/tests))
|
||||
|
||||
|
||||
# _tests should be purged during cleaning. However, we don't want it purged
|
||||
@ -142,7 +145,7 @@ include $(topsrcdir)/testing/testsuite-targets.mk
|
||||
endif
|
||||
|
||||
default all::
|
||||
$(call BUILDSTATUS,TIERS export $(if $(MOZ_PSEUDO_DERECURSE),compile )libs tools)
|
||||
$(call BUILDSTATUS,TIERS export $(if $(COMPILE_ENVIRONMENT),$(if $(MOZ_PSEUDO_DERECURSE),compile ))libs tools)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
@ -247,7 +250,7 @@ scheck::
|
||||
@relcount=`find $(DIST)/bin -name "*.so" | xargs objdump -R | grep R_386_PC32 | wc -l` && if test $$relcount -gt 0; then echo "FAILED: R_386_PC32 relocations detected in a shared library. Did you use a system header without adding it to config/system-headers?"; exit 1; else echo "PASSED"; fi
|
||||
endif
|
||||
|
||||
ifndef LIBXUL_SDK
|
||||
ifdef BUILD_JS
|
||||
js/src/Makefile: subsrcdir := js/src
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
|
@ -13,7 +13,7 @@ interface nsIAccessible;
|
||||
* A cross-platform interface that supports hyperlink-specific properties and
|
||||
* methods. Anchors, image maps, xul:labels with class="text-link" implement this interface.
|
||||
*/
|
||||
[scriptable, uuid(38c60bfa-6040-4bfe-93f2-acd6a909bb60)]
|
||||
[scriptable, uuid(883643d4-93a5-4f32-922c-6f06e01363c1)]
|
||||
interface nsIAccessibleHyperLink : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -38,16 +38,6 @@ interface nsIAccessibleHyperLink : nsISupports
|
||||
*/
|
||||
readonly attribute boolean valid;
|
||||
|
||||
/**
|
||||
* Determines whether the element currently has the focus, e. g. after
|
||||
* returning from the destination page.
|
||||
*
|
||||
* @note ARIA links can only be focused if they have the tabindex
|
||||
* attribute set. Also, state_focused should then be set on the accessible
|
||||
* for this link.
|
||||
*/
|
||||
readonly attribute boolean selected;
|
||||
|
||||
/**
|
||||
* The numbber of anchors within this Hyperlink. Is normally 1 for anchors.
|
||||
* This anchor is, for example, the visible output of the html:a tag.
|
||||
|
@ -2425,21 +2425,6 @@ Accessible::GetValid(bool *aValid)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// readonly attribute boolean nsIAccessibleHyperLink::selected
|
||||
NS_IMETHODIMP
|
||||
Accessible::GetSelected(bool *aSelected)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aSelected);
|
||||
*aSelected = false;
|
||||
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
*aSelected = IsLinkSelected();
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Accessible::AppendTextTo(nsAString& aText, uint32_t aStartOffset,
|
||||
uint32_t aLength)
|
||||
@ -2779,14 +2764,6 @@ Accessible::EndOffset()
|
||||
return hyperText ? (hyperText->GetChildOffset(this) + 1) : 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Accessible::IsLinkSelected()
|
||||
{
|
||||
NS_PRECONDITION(IsLink(),
|
||||
"IsLinkSelected() called on something that is not a hyper link!");
|
||||
return FocusMgr()->IsFocused(this);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Accessible::AnchorCount()
|
||||
{
|
||||
|
@ -629,11 +629,6 @@ public:
|
||||
return (0 == (State() & mozilla::a11y::states::INVALID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the link currently has the focus.
|
||||
*/
|
||||
bool IsLinkSelected();
|
||||
|
||||
/**
|
||||
* Return the number of anchors within the link.
|
||||
*/
|
||||
|
@ -21,9 +21,6 @@ function focusLink(aID, aSelectedAfter)
|
||||
|
||||
this.invoke = function focusLink_invoke()
|
||||
{
|
||||
is(this.accessible.selected, false,
|
||||
"Wrong selected state before focus for ID " + prettyName(aID) + "!");
|
||||
|
||||
var expectedStates = (aSelectedAfter ? STATE_FOCUSABLE : 0);
|
||||
var unexpectedStates = (!aSelectedAfter ? STATE_FOCUSABLE : 0) | STATE_FOCUSED;
|
||||
testStates(aID, expectedStates, 0, unexpectedStates, 0);
|
||||
@ -33,9 +30,6 @@ function focusLink(aID, aSelectedAfter)
|
||||
|
||||
this.finalCheck = function focusLink_finalCheck()
|
||||
{
|
||||
is(this.accessible.selected, aSelectedAfter,
|
||||
"Wrong seleccted state after focus for ID " + prettyName(aID) + "!");
|
||||
|
||||
var expectedStates = (aSelectedAfter ? STATE_FOCUSABLE | STATE_FOCUSED : 0);
|
||||
var unexpectedStates = (!aSelectedAfter ? STATE_FOCUSABLE | STATE_FOCUSED : 0);
|
||||
testStates(aID, expectedStates, 0, unexpectedStates, 0);
|
||||
|
@ -113,7 +113,7 @@ AB_CD = $(MOZ_UI_LOCALE)
|
||||
|
||||
AB := $(firstword $(subst -, ,$(AB_CD)))
|
||||
|
||||
clean clobber repackage::
|
||||
clean clobber::
|
||||
rm -rf $(DIST)/$(APP_NAME).app
|
||||
|
||||
ifdef LIBXUL_SDK
|
||||
@ -128,19 +128,16 @@ libs-preqs = \
|
||||
$(NULL)
|
||||
|
||||
.PHONY: repackage
|
||||
tools repackage:: $(libs-preqs)
|
||||
libs:: $(libs-preqs)
|
||||
rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents $(DIST)/$(APP_NAME).app --exclude English.lproj
|
||||
rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj
|
||||
sed -e "s/%MOZ_APP_VERSION%/$(MOZ_APP_VERSION)/" -e "s/%MOZ_APP_NAME%/$(MOZ_APP_NAME)/" -e "s/%APP_VERSION%/$(APP_VERSION)/" -e "s/%APP_NAME%/$(APP_NAME)/" -e "s/%APP_BINARY%/$(APP_BINARY)/" $(srcdir)/macbuild/Contents/Info.plist.in > $(DIST)/$(APP_NAME).app/Contents/Info.plist
|
||||
sed -e "s/%APP_VERSION%/$(APP_VERSION)/" -e "s/%APP_NAME%/$(APP_NAME)/" $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj/InfoPlist.strings
|
||||
rsync -a $(DIST)/bin/ $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)
|
||||
$(RM) $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/mangle $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/shlibsign
|
||||
rm -rf $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)
|
||||
ln -s $(abspath $(DIST)/bin) $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)
|
||||
ifdef LIBXUL_SDK
|
||||
cp $(LIBXUL_DIST)/bin/xulrunner$(BIN_SUFFIX) $(DIST)/$(APP_NAME).app/Contents/MacOS/$(APP_BINARY)
|
||||
rsync -a --exclude nsinstall --copy-unsafe-links $(LIBXUL_DIST)/XUL.framework $(DIST)/$(APP_NAME).app/Contents/Frameworks
|
||||
else
|
||||
$(RM) $(DIST)/$(APP_NAME).app/Contents/MacOS/$(PROGRAM)
|
||||
rsync -aL $(PROGRAM) $(DIST)/$(APP_NAME).app/Contents/MacOS
|
||||
endif
|
||||
cp -RL $(srcdir)/b2g.icns $(DIST)/$(APP_NAME).app/Contents/Resources/$(MOZ_APP_NAME).icns
|
||||
printf "APPLMOZB" > $(DIST)/$(APP_NAME).app/Contents/PkgInfo
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "1f7e70f3ad1c7a79dc03410d774e32a07093da3f",
|
||||
"revision": "207a81a6816b8627674a956b521772de2ac6b572",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -57,11 +57,6 @@ libs::
|
||||
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
|
||||
$(LOCALE_SRCDIR)/existing-profile-defaults.js > $(FINAL_TARGET)/defaults/existing-profile-defaults.js; \
|
||||
fi
|
||||
install::
|
||||
@if test -f "$(LOCALE_SRCDIR)/existing-profile-defaults.js"; then \
|
||||
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
|
||||
$(LOCALE_SRCDIR)/existing-profile-defaults.js > $(DESTDIR)$(mozappdir)/defaults/existing-profile-defaults.js; \
|
||||
fi
|
||||
|
||||
NO_JA_JP_MAC_AB_CD := $(if $(filter ja-JP-mac, $(AB_CD)),ja,$(AB_CD))
|
||||
|
||||
|
@ -120,8 +120,10 @@ endif #}
|
||||
|
||||
ifneq (,$(filter-out OS2 WINNT,$(OS_ARCH)))
|
||||
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
libs::
|
||||
cp -p $(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/bin/$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
|
||||
endif
|
||||
|
||||
GARBAGE += $(addprefix $(FINAL_TARGET)/defaults/pref/, firefox.js)
|
||||
|
||||
@ -160,7 +162,7 @@ AB_CD = $(MOZ_UI_LOCALE)
|
||||
|
||||
AB := $(firstword $(subst -, ,$(AB_CD)))
|
||||
|
||||
clean clobber repackage::
|
||||
clean clobber::
|
||||
$(RM) -r $(dist_dest)
|
||||
|
||||
ifdef LIBXUL_SDK
|
||||
@ -171,17 +173,19 @@ endif
|
||||
|
||||
MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/config/buildid)
|
||||
|
||||
.PHONY: repackage
|
||||
tools repackage:: $(PROGRAM)
|
||||
libs:: $(PROGRAM)
|
||||
$(MKDIR) -p $(dist_dest)/Contents/MacOS
|
||||
$(MKDIR) -p $(dist_dest)/Contents/Resources/$(AB).lproj
|
||||
rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents $(dist_dest) --exclude English.lproj
|
||||
rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(dist_dest)/Contents/Resources/$(AB).lproj
|
||||
sed -e "s/%APP_VERSION%/$(MOZ_APP_VERSION)/" -e "s/%MAC_APP_NAME%/$(MAC_APP_NAME)/" -e "s/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/" -e "s/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/" $(srcdir)/macbuild/Contents/Info.plist.in > $(dist_dest)/Contents/Info.plist
|
||||
sed -e "s/%MAC_APP_NAME%/$(MAC_APP_NAME)/" $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(dist_dest)/Contents/Resources/$(AB).lproj/InfoPlist.strings
|
||||
rsync -a $(DIST)/bin/ $(dist_dest)/Contents/$(APPFILES)
|
||||
rm -rf $(dist_dest)/Contents/$(APPFILES)
|
||||
ln -s $(abspath $(DIST)/bin) $(dist_dest)/Contents/$(APPFILES)
|
||||
ifdef LIBXUL_SDK
|
||||
$(RM) $(dist_dest)/Contents/MacOS/$(PROGRAM)
|
||||
rsync -aL $(PROGRAM) $(dist_dest)/Contents/MacOS
|
||||
endif
|
||||
cp -RL $(DIST)/branding/firefox.icns $(dist_dest)/Contents/Resources/firefox.icns
|
||||
cp -RL $(DIST)/branding/document.icns $(dist_dest)/Contents/Resources/document.icns
|
||||
printf APPLMOZB > $(dist_dest)/Contents/PkgInfo
|
||||
|
@ -120,14 +120,6 @@ libs:: $(addprefix generic/profile/,$(PROFILE_FILES))
|
||||
libs:: $(call MERGE_FILES,$(addprefix profile/chrome/,$(PROFILE_CHROME)))
|
||||
$(SYSINSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)/defaults/profile/chrome
|
||||
|
||||
install:: $(DESTDIR)$(mozappdir)/defaults/profile/bookmarks.html ;
|
||||
|
||||
install:: $(addprefix generic/profile/,$(PROFILE_FILES))
|
||||
$(SYSINSTALL) $(IFLAGS1) $^ $(DESTDIR)$(mozappdir)/defaults/profile
|
||||
|
||||
install:: $(call MERGE_FILES,$(addprefix profile/chrome/,$(PROFILE_CHROME)))
|
||||
$(SYSINSTALL) $(IFLAGS1) $^ $(DESTDIR)$(mozappdir)/defaults/profile/chrome
|
||||
|
||||
# metro build calls back here for search engine plugins
|
||||
searchplugins: $(addprefix $(FINAL_TARGET)/searchplugins/,$(SEARCHPLUGINS))
|
||||
.PHONY: searchplugins
|
||||
|
@ -4,6 +4,3 @@
|
||||
|
||||
export::
|
||||
$(NSINSTALL) $(srcdir)/resources.pri $(DIST)/bin
|
||||
|
||||
install::
|
||||
$(NSINSTALL) $(srcdir)/resources.pri $(DIST)/bin
|
||||
|
@ -40,9 +40,10 @@ test-ctors$(DLL_SUFFIX): DT_TYPE=INIT
|
||||
|
||||
GARBAGE += test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) test-array$(DLL_SUFFIX).bak test-ctors$(DLL_SUFFIX).bak
|
||||
|
||||
ifndef CROSS_COMPILE
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
libs:: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX)
|
||||
|
||||
ifndef CROSS_COMPILE
|
||||
dummy: dummy.$(OBJ_SUFFIX)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
@ -53,5 +54,6 @@ libs:: dummy
|
||||
|
||||
GARBAGE += dummy
|
||||
endif
|
||||
endif
|
||||
|
||||
test.$(OBJ_SUFFIX): CFLAGS := -O0
|
||||
|
@ -9,10 +9,12 @@
|
||||
MOZ_LIBSTDCXX_HOST_VERSION =
|
||||
|
||||
ifndef CROSS_COMPILE
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
ifdef USE_ELF_DYNSTR_GC
|
||||
export:: elf-dynstr-gc
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# IMPORTANT: Disable NSBUILDROOT for this directory only, otherwise we have
|
||||
# a recursive rule for finding nsinstall and the Perl scripts.
|
||||
@ -27,6 +29,7 @@ endif
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
ifneq (WINNT,$(HOST_OS_ARCH))
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
# Ensure nsinstall is atomically created
|
||||
nsinstall$(HOST_BIN_SUFFIX): $(HOST_PROGRAM)
|
||||
cp $^ $@.tmp
|
||||
@ -37,6 +40,7 @@ NSINSTALL_DEST := $(DIST)/bin
|
||||
NSINSTALL_TARGET := export
|
||||
INSTALL_TARGETS += NSINSTALL
|
||||
endif
|
||||
endif
|
||||
|
||||
HEADERS_FILES = \
|
||||
$(DEPTH)/mozilla-config.h \
|
||||
@ -111,9 +115,6 @@ GARBAGE += $(STL_WRAPPERS_SENTINEL)
|
||||
GARBAGE_DIRS += stl_wrappers
|
||||
endif
|
||||
|
||||
install::
|
||||
$(SYSINSTALL) $(IFLAGS1) $(DEPTH)/mozilla-config.h $(DESTDIR)$(includedir)
|
||||
|
||||
GARBAGE += \
|
||||
$(FINAL_LINK_COMPS) $(FINAL_LINK_LIBS) $(FINAL_LINK_COMP_NAMES) buildid $(srcdir)/*.pyc *.pyc
|
||||
|
||||
|
@ -622,8 +622,13 @@ endif
|
||||
endif
|
||||
|
||||
# Default location of include files
|
||||
ifndef LIBXUL_SDK
|
||||
IDL_PARSER_DIR = $(topsrcdir)/xpcom/idl-parser
|
||||
IDL_PARSER_CACHE_DIR = $(DEPTH)/xpcom/idl-parser
|
||||
else
|
||||
IDL_PARSER_DIR = $(LIBXUL_SDK)/sdk/bin
|
||||
IDL_PARSER_CACHE_DIR = $(LIBXUL_SDK)/sdk/bin
|
||||
endif
|
||||
|
||||
SDK_LIB_DIR = $(DIST)/sdk/lib
|
||||
SDK_BIN_DIR = $(DIST)/sdk/bin
|
||||
|
@ -49,6 +49,10 @@ idlprocess := $(PYTHON_PATH) $(PLY_INCLUDE) -I$(IDL_PARSER_DIR) -I$(IDL_PARSER_C
|
||||
$(process_py) --cache-dir $(IDL_PARSER_CACHE_DIR) $(dist_idl_dir) \
|
||||
$(dist_include_dir) $(idl_xpt_dir) $(idl_deps_dir)
|
||||
|
||||
ifdef LIBXUL_SDK
|
||||
idlprocess += -I$(LIBXUL_SDK)/idl
|
||||
endif
|
||||
|
||||
xpidl_modules := @xpidl_modules@
|
||||
|
||||
@xpidl_rules@
|
||||
|
@ -112,6 +112,7 @@ ifeq ($(CURRENT_TIER),export)
|
||||
$(addsuffix /$(CURRENT_TIER),$(filter-out config,$(CURRENT_DIRS))): config/$(CURRENT_TIER)
|
||||
endif
|
||||
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
ifneq (,$(filter libs binaries,$(CURRENT_TIER)))
|
||||
# When doing a "libs" build, target_libs.mk ensures the interesting dependency data
|
||||
# is available in the "binaries" stamp. Once recursion is done, aggregate all that
|
||||
@ -139,6 +140,8 @@ endif
|
||||
|
||||
DIST_GARBAGE += binaries-deps.mk binaries-deps
|
||||
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
# Don't recurse if MAKELEVEL is NO_RECURSE_MAKELEVEL as defined above, but
|
||||
@ -209,6 +212,8 @@ endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
|
||||
# Aggregate all dependency files relevant to a binaries build except in
|
||||
# the mozilla top-level directory.
|
||||
ifneq (_.,$(recurse_targets)_$(DEPTH))
|
||||
@ -227,4 +232,6 @@ ifneq (_.,$(recurse_targets)_$(DEPTH))
|
||||
@$(if $(or $(recurse_targets),$^),$(call py_action,link_deps,-o binaries --group-all $(if $(want_abspaths),--abspaths )--topsrcdir $(topsrcdir) --topobjdir $(DEPTH) --dist $(DIST) $(ALL_DEP_FILES)))
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
endif # ifdef MOZ_PSEUDO_DERECURSE
|
||||
|
@ -122,6 +122,7 @@ endif
|
||||
endif
|
||||
|
||||
ifdef CPP_UNIT_TESTS
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
|
||||
# Compile the tests to $(DIST)/bin. Make lots of niceties available by default
|
||||
# through TestHarness.h, by modifying the list of includes and the libs against
|
||||
@ -153,6 +154,7 @@ cppunittests-remote:
|
||||
echo "please prepare your host with environment variables for TEST_DEVICE"; \
|
||||
fi
|
||||
|
||||
endif # COMPILE_ENVIRONMENT
|
||||
endif # CPP_UNIT_TESTS
|
||||
|
||||
.PHONY: check
|
||||
@ -303,6 +305,7 @@ EXCLUDED_OBJS := $(SIMPLE_PROGRAMS:$(BIN_SUFFIX)=.$(OBJ_SUFFIX))
|
||||
SIMPLE_PROGRAMS :=
|
||||
endif
|
||||
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
ifndef TARGETS
|
||||
TARGETS = $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) $(HOST_LIBRARY) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS)
|
||||
endif
|
||||
@ -328,6 +331,19 @@ ifndef HOST_OBJS
|
||||
_HOST_OBJS = $(HOST_COBJS) $(HOST_CPPOBJS) $(HOST_CMOBJS) $(HOST_CMMOBJS)
|
||||
HOST_OBJS = $(strip $(_HOST_OBJS))
|
||||
endif
|
||||
else
|
||||
LIBRARY :=
|
||||
SHARED_LIBRARY :=
|
||||
IMPORT_LIBRARY :=
|
||||
REAL_LIBRARY :=
|
||||
PROGRAM :=
|
||||
SIMPLE_PROGRAMS :=
|
||||
HOST_LIBRARY :=
|
||||
HOST_PROGRAM :=
|
||||
HOST_SIMPLE_PROGRAMS :=
|
||||
SDK_BINARY := $(filter %.py,$(SDK_BINARY))
|
||||
SDK_LIBRARY :=
|
||||
endif
|
||||
|
||||
ALL_TRASH = \
|
||||
$(GARBAGE) $(TARGETS) $(OBJS) $(PROGOBJS) LOGS TAGS a.out \
|
||||
@ -613,7 +629,9 @@ ifndef SUPPRESS_DEFAULT_RULES
|
||||
default all::
|
||||
$(MAKE) export
|
||||
ifdef MOZ_PSEUDO_DERECURSE
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
$(MAKE) compile
|
||||
endif
|
||||
endif
|
||||
$(MAKE) libs
|
||||
$(MAKE) tools
|
||||
@ -642,11 +660,13 @@ HOST_LIBS_DEPS = $(filter %.$(LIB_SUFFIX),$(HOST_LIBS))
|
||||
GLOBAL_DEPS += Makefile $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk
|
||||
|
||||
##############################################
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
OBJ_TARGETS = $(OBJS) $(PROGOBJS) $(HOST_OBJS) $(HOST_PROGOBJS)
|
||||
|
||||
compile:: $(OBJ_TARGETS)
|
||||
|
||||
include $(topsrcdir)/config/makefiles/target_libs.mk
|
||||
include $(topsrcdir)/config/makefiles/target_binaries.mk
|
||||
endif
|
||||
|
||||
ifdef IS_TOOL_DIR
|
||||
# One would think "tools:: libs" would work, but it turns out that combined with
|
||||
@ -1353,7 +1373,7 @@ PP_TARGETS += DIST_CHROME_FILES
|
||||
endif
|
||||
|
||||
ifneq ($(XPI_PKGNAME),)
|
||||
tools realchrome::
|
||||
libs realchrome::
|
||||
ifdef STRIP_XPI
|
||||
ifndef MOZ_DEBUG
|
||||
@echo "Stripping $(XPI_PKGNAME) package directory..."
|
||||
@ -1392,7 +1412,7 @@ ifndef XPI_NAME
|
||||
$(error XPI_NAME must be set for INSTALL_EXTENSION_ID)
|
||||
endif
|
||||
|
||||
tools::
|
||||
libs::
|
||||
$(RM) -r "$(DIST)/bin$(DIST_SUBDIR:%=/%)/extensions/$(INSTALL_EXTENSION_ID)"
|
||||
$(NSINSTALL) -D "$(DIST)/bin$(DIST_SUBDIR:%=/%)/extensions/$(INSTALL_EXTENSION_ID)"
|
||||
$(call copy_dir,$(FINAL_TARGET),$(DIST)/bin$(DIST_SUBDIR:%=/%)/extensions/$(INSTALL_EXTENSION_ID))
|
||||
|
13
configure.in
13
configure.in
@ -2498,9 +2498,10 @@ esac
|
||||
if test -z "$COMPILE_ENVIRONMENT"; then
|
||||
SKIP_COMPILER_CHECKS=1
|
||||
SKIP_LIBRARY_CHECKS=1
|
||||
else
|
||||
MOZ_COMPILER_OPTS
|
||||
fi
|
||||
|
||||
MOZ_COMPILER_OPTS
|
||||
if test -z "$SKIP_COMPILER_CHECKS"; then
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
dnl ========================================================
|
||||
@ -3928,7 +3929,6 @@ MOZ_SAMPLE_TYPE_FLOAT32=
|
||||
MOZ_SAMPLE_TYPE_S16=
|
||||
MOZ_OPUS=1
|
||||
MOZ_WEBM=1
|
||||
MOZ_DASH=
|
||||
MOZ_DIRECTSHOW=
|
||||
MOZ_WMF=
|
||||
MOZ_WEBRTC=1
|
||||
@ -7179,7 +7179,7 @@ if test "$USE_ELF_HACK" = 1; then
|
||||
esac
|
||||
fi
|
||||
|
||||
if test -n "$USE_ELF_HACK"; then
|
||||
if test -n "$COMPILE_ENVIRONMENT" -a -n "$USE_ELF_HACK"; then
|
||||
dnl PT_GNU_RELRO segment makes the dynamic linker set a read-only flag on
|
||||
dnl memory addresses it maps to. The result is that by the time elfhack
|
||||
dnl kicks in, it is not possible to apply relocations because of that,
|
||||
@ -7874,7 +7874,9 @@ AC_SUBST(MOZ_PIXMAN_CFLAGS)
|
||||
AC_SUBST(MOZ_PIXMAN_LIBS)
|
||||
|
||||
# Check for headers defining standard int types.
|
||||
MOZ_CHECK_HEADERS(stdint.h inttypes.h sys/int_types.h)
|
||||
if test -n "$COMPILE_ENVIRONMENT"; then
|
||||
MOZ_CHECK_HEADERS(stdint.h inttypes.h sys/int_types.h)
|
||||
fi
|
||||
|
||||
if test "$MOZ_TREE_CAIRO"; then
|
||||
MOZ_CAIRO_CFLAGS='-I$(LIBXUL_DIST)/include/cairo'
|
||||
@ -8630,7 +8632,6 @@ AC_SUBST(MOZ_VORBIS)
|
||||
AC_SUBST(MOZ_TREMOR)
|
||||
AC_SUBST(MOZ_OPUS)
|
||||
AC_SUBST(MOZ_WEBM)
|
||||
AC_SUBST(MOZ_DASH)
|
||||
AC_SUBST(MOZ_WMF)
|
||||
AC_SUBST(MOZ_DIRECTSHOW)
|
||||
AC_SUBST(MOZ_MEDIA_PLUGINS)
|
||||
@ -8661,6 +8662,7 @@ AC_SUBST(MOZ_FOLD_LIBS)
|
||||
AC_SUBST(MOZ_ENABLE_SZIP)
|
||||
AC_SUBST(MOZ_SZIP_FLAGS)
|
||||
|
||||
if test -n "$COMPILE_ENVIRONMENT"; then
|
||||
AC_MSG_CHECKING([for posix_fallocate])
|
||||
AC_TRY_LINK([#define _XOPEN_SOURCE 600
|
||||
#include <fcntl.h>],
|
||||
@ -8676,7 +8678,6 @@ else
|
||||
fi
|
||||
|
||||
dnl Check for missing components
|
||||
if test "$COMPILE_ENVIRONMENT"; then
|
||||
if test "$MOZ_X11"; then
|
||||
if test "$WITHOUT_X11"; then
|
||||
AC_MSG_ERROR([--without-x specified and MOZ_X11 still defined])
|
||||
|
@ -129,14 +129,6 @@ public:
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
virtual nsresult OpenByteRange(nsIStreamListener** aStreamListener,
|
||||
MediaByteRange const &aByteRange)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
|
||||
{
|
||||
aRanges.AppendElement(MediaByteRange(0, mLength));
|
||||
|
@ -49,9 +49,6 @@
|
||||
#include "RtspOmxDecoder.h"
|
||||
#include "RtspOmxReader.h"
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
#include "DASHDecoder.h"
|
||||
#endif
|
||||
#ifdef MOZ_WMF
|
||||
#include "WMFDecoder.h"
|
||||
#include "WMFReader.h"
|
||||
@ -285,24 +282,6 @@ IsMediaPluginsType(const nsACString& aType)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
/* static */
|
||||
static const char* const gDASHMPDTypes[2] = {
|
||||
"application/dash+xml",
|
||||
nullptr
|
||||
};
|
||||
|
||||
static bool
|
||||
IsDASHMPDType(const nsACString& aType)
|
||||
{
|
||||
if (!MediaDecoder::IsDASHEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CodecListContains(gDASHMPDTypes, aType);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WMF
|
||||
static bool
|
||||
IsWMFSupportedType(const nsACString& aType)
|
||||
@ -399,13 +378,6 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType,
|
||||
result = CANPLAY_YES;
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
if (IsDASHMPDType(nsDependentCString(aMIMEType))) {
|
||||
// DASH manifest uses WebM codecs only.
|
||||
codecList = gWebMCodecs;
|
||||
result = CANPLAY_YES;
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_GSTREAMER
|
||||
if (GStreamerDecoder::CanHandleMediaType(nsDependentCString(aMIMEType),
|
||||
aHaveRequestedCodecs ? &aRequestedCodecs : nullptr)) {
|
||||
@ -530,11 +502,6 @@ DecoderTraits::CreateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
||||
decoder = new WebMDecoder();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
if (IsDASHMPDType(aType)) {
|
||||
decoder = new DASHDecoder();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
// Note: DirectShow decoder must come before WMFDecoder, else the pref
|
||||
// "media.directshow.preferred" won't be honored.
|
||||
@ -616,9 +583,6 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
|
||||
if (IsAppleMediaSupportedType(aType)) {
|
||||
decoderReader = new AppleMP3Reader(aDecoder);
|
||||
} else
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
// The DASH decoder is not supported.
|
||||
#endif
|
||||
if (false) {} // dummy if to take care of the dangling else
|
||||
|
||||
@ -640,9 +604,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
|
||||
#ifdef MOZ_WEBM
|
||||
IsWebMType(aType) ||
|
||||
#endif
|
||||
#ifdef MOZ_DASH
|
||||
IsDASHMPDType(aType) ||
|
||||
#endif
|
||||
#ifdef MOZ_GSTREAMER
|
||||
IsGStreamerSupportedType(aType) ||
|
||||
#endif
|
||||
|
@ -1739,14 +1739,6 @@ MediaDecoder::IsMediaPluginsEnabled()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
bool
|
||||
MediaDecoder::IsDASHEnabled()
|
||||
{
|
||||
return Preferences::GetBool("media.dash.enabled");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WMF
|
||||
bool
|
||||
MediaDecoder::IsWMFEnabled()
|
||||
|
@ -787,10 +787,6 @@ public:
|
||||
static bool IsMediaPluginsEnabled();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
static bool IsDASHEnabled();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WMF
|
||||
static bool IsWMFEnabled();
|
||||
#endif
|
||||
|
@ -488,11 +488,6 @@ public:
|
||||
// or an un-recoverable read error has occured.
|
||||
virtual bool DecodeAudioData() = 0;
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
// Steps to carry out at the start of the |DecodeLoop|.
|
||||
virtual void PrepareToDecode() { }
|
||||
#endif
|
||||
|
||||
// Reads and decodes one video frame. Packets with a timestamp less
|
||||
// than aTimeThreshold will be decoded (unless they're not keyframes
|
||||
// and aKeyframeSkip is true), but will not be added to the queue.
|
||||
|
@ -853,10 +853,6 @@ void MediaDecoderStateMachine::DecodeLoop()
|
||||
!mStopDecodeThread &&
|
||||
(videoPlaying || audioPlaying))
|
||||
{
|
||||
#ifdef MOZ_DASH
|
||||
mReader->PrepareToDecode();
|
||||
#endif
|
||||
|
||||
// We don't want to consider skipping to the next keyframe if we've
|
||||
// only just started up the decode loop, so wait until we've decoded
|
||||
// some frames before enabling the keyframe skip logic on video.
|
||||
|
@ -59,12 +59,6 @@ ChannelMediaResource::ChannelMediaResource(MediaDecoder* aDecoder,
|
||||
mLock("ChannelMediaResource.mLock"),
|
||||
mIgnoreResume(false),
|
||||
mSeekingForMetadata(false),
|
||||
#ifdef MOZ_DASH
|
||||
mByteRangeDownloads(false),
|
||||
mByteRangeFirstOpen(true),
|
||||
mSeekOffsetMonitor("media.dashseekmonitor"),
|
||||
mSeekOffset(-1),
|
||||
#endif
|
||||
mIsTransportSeekable(true)
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
@ -417,16 +411,6 @@ ChannelMediaResource::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
|
||||
mChannelStatistics->Stop();
|
||||
}
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
// If we were loading a byte range, notify decoder and return.
|
||||
// Skip this for unterminated byte range requests, e.g. seeking for whole
|
||||
// file downloads.
|
||||
if (mByteRangeDownloads) {
|
||||
mDecoder->NotifyDownloadEnded(aStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Note that aStatus might have succeeded --- this might be a normal close
|
||||
// --- even in situations where the server cut us off because we were
|
||||
// suspended. So we need to "reopen on error" in that case too. The only
|
||||
@ -490,18 +474,6 @@ ChannelMediaResource::CopySegmentToCache(nsIInputStream *aInStream,
|
||||
|
||||
closure->mResource->mDecoder->NotifyDataArrived(aFromSegment, aCount, closure->mResource->mOffset);
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
// For byte range downloads controlled by |DASHDecoder|, there are cases in
|
||||
// which the reader's offset is different enough from the channel offset that
|
||||
// |MediaCache| requests a |CacheClientSeek| to the reader's offset. This
|
||||
// can happen between calls to |CopySegmentToCache|. To avoid copying at
|
||||
// incorrect offsets, ensure |MediaCache| copies to the location that
|
||||
// |ChannelMediaResource| expects.
|
||||
if (closure->mResource->mByteRangeDownloads) {
|
||||
closure->mResource->mCacheStream.NotifyDataStarted(closure->mResource->mOffset);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Keep track of where we're up to.
|
||||
LOG("%p [ChannelMediaResource]: CopySegmentToCache at mOffset [%lld] add "
|
||||
"[%d] bytes for decoder[%p]",
|
||||
@ -548,39 +520,6 @@ ChannelMediaResource::OnDataAvailable(nsIRequest* aRequest,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
/* |OpenByteRange|
|
||||
* For terminated byte range requests, use this function.
|
||||
* Callback is |MediaDecoder|::|NotifyByteRangeDownloaded|().
|
||||
* See |CacheClientSeek| also.
|
||||
*/
|
||||
|
||||
nsresult
|
||||
ChannelMediaResource::OpenByteRange(nsIStreamListener** aStreamListener,
|
||||
MediaByteRange const & aByteRange)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
mByteRangeDownloads = true;
|
||||
mByteRange = aByteRange;
|
||||
|
||||
// OpenByteRange may be called multiple times; same URL, different ranges.
|
||||
// For the first call using this URL, forward to Open for some init.
|
||||
if (mByteRangeFirstOpen) {
|
||||
mByteRangeFirstOpen = false;
|
||||
return Open(aStreamListener);
|
||||
}
|
||||
|
||||
// For subsequent calls, ensure channel is recreated with correct byte range.
|
||||
CloseChannel();
|
||||
|
||||
nsresult rv = RecreateChannel();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return OpenChannel(aStreamListener);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult ChannelMediaResource::Open(nsIStreamListener **aStreamListener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
@ -832,13 +771,6 @@ nsresult ChannelMediaResource::Seek(int32_t aWhence, int64_t aOffset)
|
||||
|
||||
CMLOG("Seek requested for aOffset [%lld] for decoder [%p]",
|
||||
aOffset, mDecoder);
|
||||
#ifdef MOZ_DASH
|
||||
// Remember |aOffset|, because Media Cache may request a diff offset later.
|
||||
if (mByteRangeDownloads) {
|
||||
ReentrantMonitorAutoEnter mon(mSeekOffsetMonitor);
|
||||
mSeekOffset = aOffset;
|
||||
}
|
||||
#endif
|
||||
return mCacheStream.Seek(aWhence, aOffset);
|
||||
}
|
||||
|
||||
@ -1045,22 +977,7 @@ ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
|
||||
CMLOG("CacheClientSeek requested for aOffset [%lld] for decoder [%p]",
|
||||
aOffset, mDecoder);
|
||||
|
||||
#ifndef MOZ_DASH
|
||||
CloseChannel();
|
||||
#else
|
||||
// |CloseChannel| immediately for non-byte-range downloads.
|
||||
if (!mByteRangeDownloads) {
|
||||
CloseChannel();
|
||||
} else if (mChannel) {
|
||||
// Only close byte range channels if they are not in pending state.
|
||||
bool isPending = false;
|
||||
nsresult rv = mChannel->IsPending(&isPending);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!isPending) {
|
||||
CloseChannel();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aResume) {
|
||||
NS_ASSERTION(mSuspendCount > 0, "Too many resumes!");
|
||||
@ -1068,85 +985,6 @@ ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
|
||||
--mSuspendCount;
|
||||
}
|
||||
|
||||
#ifdef MOZ_DASH // Note: For chunked downloads, e.g. DASH, we need to determine which chunk
|
||||
// contains the requested offset, |mOffset|. This is either previously
|
||||
// requested in |Seek| or updated to the most recent bytes downloaded.
|
||||
// So the process below is:
|
||||
// 1 - Query decoder for chunk containing desired offset, |mOffset|.
|
||||
// Return silently if the offset is not available; suggests decoder is
|
||||
// yet to get range information.
|
||||
// Return with NetworkError for all other errors.
|
||||
//
|
||||
// 2 - Adjust |mByteRange|.mStart to |aOffset|, requested by media cache.
|
||||
// For seeking, the media cache always requests the start of the cache
|
||||
// block, so we need to adjust the first chunk of a seek.
|
||||
// E.g. For "DASH-WebM On Demand" this means the first chunk after
|
||||
// seeking will most likely be larger than the subsegment (cluster).
|
||||
//
|
||||
// 3 - Call |OpenByteRange| requesting |mByteRange| bytes.
|
||||
|
||||
if (mByteRangeDownloads) {
|
||||
// Query decoder for chunk containing desired offset.
|
||||
nsresult rv;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mSeekOffsetMonitor);
|
||||
// Only continue with seek request if a prior call to |Seek| was made.
|
||||
// If |Seek| was not called previously, it means the media cache is
|
||||
// seeking on its own.
|
||||
// E.g. For those WebM files which are encoded with cues at the end of
|
||||
// the file, when the cues are parsed, the reader and media cache
|
||||
// automatically return to the first offset not downloaded, normally the
|
||||
// first byte after init data. This results in |MediaCache| requesting
|
||||
// |aOffset| = 0 (aligning to the start of the cache block. Ignore this
|
||||
// and let |DASHDecoder| decide which bytes to download and when.
|
||||
if (mSeekOffset >= 0) {
|
||||
rv = mDecoder->GetByteRangeForSeek(mSeekOffset, mByteRange);
|
||||
// Cache may try to seek from the next uncached byte: this offset may
|
||||
// be after the byte range being seeked, i.e. the range containing
|
||||
// |mSeekOffset|, which is the offset actually requested by the reader.
|
||||
// This case means that the seeked range is already cached. For byte
|
||||
// range downloads, we do not permit the cache to request bytes outside
|
||||
// the seeked range. Instead, the decoder is responsible for
|
||||
// controlling the sequence of byte range downloads. As such, return
|
||||
// silently, and do NOT request a new download.
|
||||
if (NS_SUCCEEDED(rv) && !mByteRange.IsNull() &&
|
||||
aOffset > mByteRange.mEnd) {
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
mByteRange.Clear();
|
||||
}
|
||||
mSeekOffset = -1;
|
||||
} else if (mByteRange.mStart <= aOffset && aOffset <= mByteRange.mEnd) {
|
||||
CMLOG("Trying to resume download at offset [%lld].", aOffset);
|
||||
rv = NS_OK;
|
||||
} else {
|
||||
CMLOG("MediaCache [%p] trying to seek independently to offset [%lld].",
|
||||
&mCacheStream, aOffset);
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
// Decoder will not make byte ranges available for non-active streams, or
|
||||
// if range information is not yet available, or for metadata bytes if
|
||||
// they have already been downloaded and read. In all cases, it is ok to
|
||||
// return silently and assume that the decoder will request the correct
|
||||
// byte range when range information becomes available.
|
||||
CMLOG("Byte range not available for decoder [%p]; returning "
|
||||
"silently.", mDecoder);
|
||||
return NS_OK;
|
||||
} else if (NS_FAILED(rv) || mByteRange.IsNull()) {
|
||||
// Decoder reported an error we don't want to handle here; just return.
|
||||
CMLOG("Error getting byte range: seek offset[%lld] cache offset[%lld] "
|
||||
"decoder[%p]", mSeekOffset, aOffset, mDecoder);
|
||||
mDecoder->NetworkError();
|
||||
CloseChannel();
|
||||
return rv;
|
||||
}
|
||||
// Adjust the byte range to start where the media cache requested.
|
||||
mByteRange.mStart = mOffset = aOffset;
|
||||
return OpenByteRange(nullptr, mByteRange);
|
||||
}
|
||||
#endif
|
||||
|
||||
mOffset = aOffset;
|
||||
|
||||
if (mSuspendCount > 0) {
|
||||
|
@ -7,9 +7,6 @@
|
||||
#define MediaResource_h_
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#ifdef MOZ_DASH
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#endif
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIStreamingProtocolController.h"
|
||||
@ -362,19 +359,6 @@ public:
|
||||
*/
|
||||
virtual nsresult Open(nsIStreamListener** aStreamListener) = 0;
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
/**
|
||||
* Open the stream using a specific byte range only. Creates a stream
|
||||
* listener and returns it in aStreamListener; this listener needs to be
|
||||
* notified of incoming data. Byte range is specified in aByteRange.
|
||||
*/
|
||||
virtual nsresult OpenByteRange(nsIStreamListener** aStreamListener,
|
||||
MediaByteRange const &aByteRange)
|
||||
{
|
||||
return Open(aStreamListener);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Fills aRanges with MediaByteRanges representing the data which is cached
|
||||
* in the media cache. Stream should be pinned during call and while
|
||||
@ -522,10 +506,6 @@ public:
|
||||
|
||||
// Main thread
|
||||
virtual nsresult Open(nsIStreamListener** aStreamListener);
|
||||
#ifdef MOZ_DASH
|
||||
virtual nsresult OpenByteRange(nsIStreamListener** aStreamListener,
|
||||
MediaByteRange const & aByteRange);
|
||||
#endif
|
||||
virtual nsresult Close();
|
||||
virtual void Suspend(bool aCloseImmediately);
|
||||
virtual void Resume();
|
||||
@ -667,20 +647,6 @@ protected:
|
||||
// Start and end offset of the bytes to be requested.
|
||||
MediaByteRange mByteRange;
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
// True if resource was opened with a byte rage request.
|
||||
bool mByteRangeDownloads;
|
||||
|
||||
// Set to false once first byte range request has been made.
|
||||
bool mByteRangeFirstOpen;
|
||||
|
||||
// For byte range requests, set to the offset requested in |Seek|.
|
||||
// Used in |CacheClientSeek| to find the originally requested byte range.
|
||||
// Read/Write on multiple threads; use |mSeekMonitor|.
|
||||
ReentrantMonitor mSeekOffsetMonitor;
|
||||
int64_t mSeekOffset;
|
||||
#endif
|
||||
|
||||
// True if the stream can seek into unbuffered ranged, i.e. if the
|
||||
// connection supports byte range requests.
|
||||
bool mIsTransportSeekable;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,412 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
|
||||
/* This Source Code Form Is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* DASH - Dynamic Adaptive Streaming over HTTP
|
||||
*
|
||||
* DASH is an adaptive bitrate streaming technology where a multimedia file is
|
||||
* partitioned into one or more segments and delivered to a client using HTTP.
|
||||
*
|
||||
* see DASHDecoder.cpp for info on DASH interaction with the media engine.*/
|
||||
|
||||
#if !defined(DASHDecoder_h_)
|
||||
#define DASHDecoder_h_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsITimer.h"
|
||||
#include "MediaDecoder.h"
|
||||
#include "DASHReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
class IMPDManager;
|
||||
class nsDASHMPDParser;
|
||||
class Representation;
|
||||
}// net
|
||||
|
||||
class DASHRepDecoder;
|
||||
|
||||
class DASHDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
typedef class mozilla::net::IMPDManager IMPDManager;
|
||||
typedef class mozilla::net::nsDASHMPDParser nsDASHMPDParser;
|
||||
typedef class mozilla::net::Representation Representation;
|
||||
|
||||
// XXX Arbitrary max file size for MPD. 50MB seems generously large.
|
||||
static const uint32_t DASH_MAX_MPD_SIZE = 50*1024*1024;
|
||||
|
||||
DASHDecoder();
|
||||
~DASHDecoder();
|
||||
|
||||
MediaDecoder* Clone() MOZ_OVERRIDE {
|
||||
if (!IsDASHEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new DASHDecoder();
|
||||
}
|
||||
|
||||
// Creates a single state machine for all stream decoders.
|
||||
// Called from Load on the main thread only.
|
||||
MediaDecoderStateMachine* CreateStateMachine();
|
||||
|
||||
// Loads the MPD from the network and subsequently loads the media streams.
|
||||
// Called from the main thread only.
|
||||
virtual nsresult Load(nsIStreamListener** aListener,
|
||||
MediaDecoder* aCloneDonor) MOZ_OVERRIDE;
|
||||
|
||||
// Notifies download of MPD file has ended.
|
||||
// Called on the main thread only.
|
||||
void NotifyDownloadEnded(nsresult aStatus);
|
||||
|
||||
// Notification from |DASHReader| that a seek has occurred in
|
||||
// |aSubsegmentIdx|. Passes notification onto subdecoder which downloaded
|
||||
// the subsegment already, if download is in the past. Otherwise, it returns.
|
||||
void NotifySeekInVideoSubsegment(int32_t aRepDecoderIdx,
|
||||
int32_t aSubsegmentIdx);
|
||||
void NotifySeekInAudioSubsegment(int32_t aSubsegmentIdx);
|
||||
|
||||
// Notifies that a byte range download has ended. As per the DASH spec, this
|
||||
// allows for stream switching at the boundaries of the byte ranges.
|
||||
// Called on the main thread only.
|
||||
void NotifyDownloadEnded(DASHRepDecoder* aRepDecoder,
|
||||
nsresult aStatus,
|
||||
int32_t const aSubsegmentIdx);
|
||||
|
||||
// Notification from an |MediaDecoderReader| class that metadata has been
|
||||
// read. Declared here to allow overloading.
|
||||
void OnReadMetadataCompleted() MOZ_OVERRIDE { }
|
||||
|
||||
// Seeks to aTime in seconds
|
||||
nsresult Seek(double aTime) MOZ_OVERRIDE;
|
||||
|
||||
// Notification from |DASHRepDecoder| that a metadata has been read.
|
||||
// |DASHDecoder| will initiate load of data bytes for active audio/video
|
||||
// decoders. Called on the decode thread.
|
||||
void OnReadMetadataCompleted(DASHRepDecoder* aRepDecoder);
|
||||
|
||||
// Returns true if all subsegments from current decode position are
|
||||
// downloaded. Must be in monitor. Call from any thread.
|
||||
bool IsDataCachedToEndOfResource() MOZ_OVERRIDE;
|
||||
|
||||
// Refers to downloading data bytes, i.e. non metadata.
|
||||
// Returns true if |aRepDecoder| is an active audio or video sub decoder AND
|
||||
// if metadata for all audio or video decoders has been read.
|
||||
// Could be called from any thread; enters decoder monitor.
|
||||
bool IsDecoderAllowedToDownloadData(DASHRepDecoder* aRepDecoder);
|
||||
|
||||
// Refers to downloading data bytes during SEEKING.
|
||||
// Returns true if |aRepDecoder| is the active audio sub decoder, OR if
|
||||
// it is a video decoder and is allowed to download this subsegment.
|
||||
// Returns false if there is still some metadata to download.
|
||||
// Could be called from any thread; enters decoder monitor.
|
||||
bool IsDecoderAllowedToDownloadSubsegment(DASHRepDecoder* aRepDecoder,
|
||||
int32_t const aSubsegmentIdx);
|
||||
|
||||
// Determines if rep/sub decoders should be switched, and if so switches
|
||||
// them. Notifies |DASHReader| if and when it should switch readers.
|
||||
// Returns a pointer to the new active decoder.
|
||||
// Called on the main thread.
|
||||
nsresult PossiblySwitchDecoder(DASHRepDecoder* aRepDecoder);
|
||||
|
||||
// Sets the byte range index for audio|video downloads. Will only increment
|
||||
// for current active decoders. Could be called from any thread.
|
||||
// Requires monitor because of write to |mAudioSubsegmentIdx| or
|
||||
// |mVideoSubsegmentIdx|.
|
||||
void SetSubsegmentIndex(DASHRepDecoder* aRepDecoder,
|
||||
int32_t aSubsegmentIdx);
|
||||
|
||||
// Suspend any media downloads that are in progress. Called by the
|
||||
// media element when it is sent to the bfcache, or when we need
|
||||
// to throttle the download. Call on the main thread only. This can
|
||||
// be called multiple times, there's an internal "suspend count".
|
||||
void Suspend() MOZ_OVERRIDE;
|
||||
|
||||
// Resume any media downloads that have been suspended. Called by the
|
||||
// media element when it is restored from the bfcache, or when we need
|
||||
// to stop throttling the download. Call on the main thread only.
|
||||
// The download will only actually resume once as many Resume calls
|
||||
// have been made as Suspend calls. When aForceBuffering is true,
|
||||
// we force the decoder to go into buffering state before resuming
|
||||
// playback.
|
||||
void Resume(bool aForceBuffering) MOZ_OVERRIDE;
|
||||
private:
|
||||
// Increments the byte range index for audio|video downloads. Will only
|
||||
// increment for current active decoders. Could be called from any thread.
|
||||
// Requires monitor because of write to |mAudioSubsegmentIdx| or
|
||||
// |mVideoSubsegmentIdx|.
|
||||
void IncrementSubsegmentIndex(DASHRepDecoder* aRepDecoder)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
if (aRepDecoder == AudioRepDecoder()) {
|
||||
mAudioSubsegmentIdx++;
|
||||
} else if (aRepDecoder == VideoRepDecoder()) {
|
||||
mVideoSubsegmentIdx++;
|
||||
}
|
||||
}
|
||||
public:
|
||||
// Gets the byte range index for audio|video downloads. Will only increment
|
||||
// for current active decoders. Could be called from any thread. Will enter
|
||||
// monitor for read access off the decode thread.
|
||||
int32_t GetSubsegmentIndex(DASHRepDecoder* aRepDecoder)
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
||||
GetReentrantMonitor());
|
||||
if (aRepDecoder == AudioRepDecoder()) {
|
||||
return mAudioSubsegmentIdx;
|
||||
} else if (aRepDecoder == VideoRepDecoder()) {
|
||||
return mVideoSubsegmentIdx;
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
// Returns the total number of subsegments that have been loaded. Will enter
|
||||
// monitor for read access off the decode thread.
|
||||
uint32_t GetNumSubsegmentLoads() {
|
||||
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
||||
GetReentrantMonitor());
|
||||
return mVideoSubsegmentLoads.Length();
|
||||
}
|
||||
|
||||
// Returns the index of the rep decoder used to load a subsegment. Will enter
|
||||
// monitor for read access off the decode thread.
|
||||
int32_t GetRepIdxForVideoSubsegmentLoad(int32_t aSubsegmentIdx)
|
||||
{
|
||||
NS_ASSERTION(0 <= aSubsegmentIdx, "Subsegment index should not be negative.");
|
||||
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
||||
GetReentrantMonitor());
|
||||
if ((uint32_t)aSubsegmentIdx < mVideoSubsegmentLoads.Length()) {
|
||||
return mVideoSubsegmentLoads[aSubsegmentIdx];
|
||||
} else {
|
||||
// If it hasn't been downloaded yet, use the lowest bitrate decoder.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the index of the rep decoder used to load a subsegment, after a
|
||||
// seek. Called on the decode thread, and will block if the subsegment
|
||||
// previous to the one specified has not yet been loaded. This ensures that
|
||||
// |DASHDecoder| has had a chance to determine which decoder should load the
|
||||
// next subsegment, in the case where |DASHRepReader|::|DecodeToTarget| has
|
||||
// read all the data for the current subsegment from the cache, and needs to
|
||||
// know which reader (including itself) to use next.
|
||||
int32_t GetRepIdxForVideoSubsegmentLoadAfterSeek(int32_t aSubsegmentIndex);
|
||||
|
||||
int32_t GetSwitchCountAtVideoSubsegment(int32_t aSubsegmentIdx)
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
||||
GetReentrantMonitor());
|
||||
NS_ASSERTION(0 <= aSubsegmentIdx, "Subsegment index should not be negative.");
|
||||
if (aSubsegmentIdx == 0) {
|
||||
// Do the zeroeth switch next.
|
||||
return 0;
|
||||
}
|
||||
int32_t switchCount = 0;
|
||||
for (uint32_t i = 1;
|
||||
i < mVideoSubsegmentLoads.Length() &&
|
||||
i <= (uint32_t)aSubsegmentIdx;
|
||||
i++) {
|
||||
if (mVideoSubsegmentLoads[i-1] != mVideoSubsegmentLoads[i]) {
|
||||
switchCount++;
|
||||
}
|
||||
}
|
||||
return switchCount;
|
||||
}
|
||||
|
||||
// The actual playback rate computation. The monitor must be held.
|
||||
// XXX Computes playback for the current video rep decoder only.
|
||||
double ComputePlaybackRate(bool* aReliable) MOZ_OVERRIDE;
|
||||
|
||||
// Something has changed that could affect the computed playback rate,
|
||||
// so recompute it. The monitor must be held. Will be forwarded to current
|
||||
// audio and video rep decoders.
|
||||
void UpdatePlaybackRate() MOZ_OVERRIDE;
|
||||
|
||||
// Stop updating the bytes downloaded for progress notifications. Called
|
||||
// when seeking to prevent wild changes to the progress notification.
|
||||
// Forwarded to sub-decoders. Must be called with the decoder monitor held.
|
||||
void StopProgressUpdates() MOZ_OVERRIDE;
|
||||
|
||||
// Allow updating the bytes downloaded for progress notifications.
|
||||
// Forwarded to sub-decoders. Must be called with the decoder monitor held.
|
||||
void StartProgressUpdates() MOZ_OVERRIDE;
|
||||
|
||||
// Used to estimate rates of data passing through the decoder's channel.
|
||||
// Records activity starting on the channel. The monitor must be held.
|
||||
virtual void NotifyPlaybackStarted() MOZ_OVERRIDE;
|
||||
|
||||
// Used to estimate rates of data passing through the decoder's channel.
|
||||
// Records activity stopping on the channel. The monitor must be held.
|
||||
virtual void NotifyPlaybackStopped() MOZ_OVERRIDE;
|
||||
|
||||
// Return statistics. This is used for progress events and other things.
|
||||
// This can be called from any thread. It's only a snapshot of the
|
||||
// current state, since other threads might be changing the state
|
||||
// at any time.
|
||||
// XXX Stats are calculated based on the current video rep decoder, with the
|
||||
// exception of download rate, which is based on all video downloads.
|
||||
virtual Statistics GetStatistics() MOZ_OVERRIDE;
|
||||
|
||||
// Drop reference to state machine and tell sub-decoders to do the same.
|
||||
// Only called during shutdown dance, on main thread only.
|
||||
void ReleaseStateMachine();
|
||||
|
||||
// Overridden to forward |Shutdown| to sub-decoders.
|
||||
// Called on the main thread only.
|
||||
void Shutdown();
|
||||
|
||||
// Called by sub-decoders when load has been aborted. Will notify media
|
||||
// element only once. Called on the main thread only.
|
||||
void LoadAborted();
|
||||
|
||||
// Notifies the element that decoding has failed. On main thread, call is
|
||||
// forwarded to |MediaDecoder|::|Error| immediately. On other threads,
|
||||
// a call is dispatched for execution on the main thread.
|
||||
void DecodeError();
|
||||
|
||||
private:
|
||||
// Reads the MPD data from resource to a byte stream.
|
||||
// Called on the MPD reader thread.
|
||||
void ReadMPDBuffer();
|
||||
|
||||
// Called when MPD data is completely read.
|
||||
// On the main thread.
|
||||
void OnReadMPDBufferCompleted();
|
||||
|
||||
// Parses the copied MPD byte stream.
|
||||
// On the main thread: DOM APIs complain when off the main thread.
|
||||
nsresult ParseMPDBuffer();
|
||||
|
||||
// Creates the sub-decoders for a |Representation|, i.e. media streams.
|
||||
// On the main thread.
|
||||
nsresult CreateRepDecoders();
|
||||
|
||||
// Creates audio/video decoders for individual |Representation|s.
|
||||
// On the main thread.
|
||||
nsresult CreateAudioRepDecoder(nsIURI* aUrl, Representation const * aRep);
|
||||
nsresult CreateVideoRepDecoder(nsIURI* aUrl, Representation const * aRep);
|
||||
|
||||
// Get audio sub-decoder for current audio |Representation|. Will return
|
||||
// nullptr for out of range indexes.
|
||||
// Enters monitor for read access off the decode thread.
|
||||
// XXX Note: Although an array of audio decoders is provided, audio stream
|
||||
// switching is not yet supported.
|
||||
DASHRepDecoder* AudioRepDecoder() {
|
||||
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
||||
GetReentrantMonitor());
|
||||
if (0 == mAudioRepDecoders.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ENSURE_TRUE((uint32_t)mAudioRepDecoderIdx < mAudioRepDecoders.Length(),
|
||||
nullptr);
|
||||
if (mAudioRepDecoderIdx < 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return mAudioRepDecoders[mAudioRepDecoderIdx];
|
||||
}
|
||||
}
|
||||
|
||||
// Get video sub-decoder for current video |Representation|. Will return
|
||||
// nullptr for out of range indexes.
|
||||
// Enters monitor for read access off the decode thread.
|
||||
DASHRepDecoder* VideoRepDecoder() {
|
||||
ReentrantMonitorConditionallyEnter mon(!OnDecodeThread(),
|
||||
GetReentrantMonitor());
|
||||
if (0 == mVideoRepDecoders.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ENSURE_TRUE((uint32_t)mVideoRepDecoderIdx < mVideoRepDecoders.Length(),
|
||||
nullptr);
|
||||
if (mVideoRepDecoderIdx < 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return mVideoRepDecoders[mVideoRepDecoderIdx];
|
||||
}
|
||||
}
|
||||
|
||||
// Creates audio/video resources for individual |Representation|s.
|
||||
// On the main thread.
|
||||
MediaResource* CreateAudioSubResource(nsIURI* aUrl,
|
||||
MediaDecoder* aAudioDecoder);
|
||||
MediaResource* CreateVideoSubResource(nsIURI* aUrl,
|
||||
MediaDecoder* aVideoDecoder);
|
||||
|
||||
// Creates an http channel for a |Representation|.
|
||||
// On the main thread.
|
||||
nsresult CreateSubChannel(nsIURI* aUrl, nsIChannel** aChannel);
|
||||
|
||||
// Loads the media |Representations|, i.e. the media streams.
|
||||
// On the main thread.
|
||||
nsresult LoadRepresentations();
|
||||
|
||||
// True when media element has already been notified of an aborted load.
|
||||
bool mNotifiedLoadAborted;
|
||||
|
||||
// Ptr for the MPD data.
|
||||
nsAutoArrayPtr<char> mBuffer;
|
||||
// Length of the MPD data.
|
||||
uint32_t mBufferLength;
|
||||
// Ptr to the MPD Reader thread.
|
||||
nsCOMPtr<nsIThread> mMPDReaderThread;
|
||||
// Document Principal.
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
// MPD Manager provides access to the MPD information.
|
||||
nsAutoPtr<IMPDManager> mMPDManager;
|
||||
|
||||
// Main reader object; manages all sub-readers for |Representation|s. Owned by
|
||||
// state machine; destroyed in state machine's destructor.
|
||||
DASHReader* mDASHReader;
|
||||
|
||||
// Sub-decoder vars. Note: For all following members, the decode monitor
|
||||
// should be held for write access on decode thread, and all read/write off
|
||||
// the decode thread.
|
||||
|
||||
// Index of the video |AdaptationSet|.
|
||||
int32_t mVideoAdaptSetIdx;
|
||||
|
||||
// Indexes for the current audio and video decoders.
|
||||
int32_t mAudioRepDecoderIdx;
|
||||
int32_t mVideoRepDecoderIdx;
|
||||
|
||||
// Array of pointers for the |Representation|s in the audio/video
|
||||
// |AdaptationSet|.
|
||||
nsTArray<nsRefPtr<DASHRepDecoder> > mAudioRepDecoders;
|
||||
nsTArray<nsRefPtr<DASHRepDecoder> > mVideoRepDecoders;
|
||||
|
||||
// Current index of subsegments downloaded for audio/video decoder.
|
||||
int32_t mAudioSubsegmentIdx;
|
||||
int32_t mVideoSubsegmentIdx;
|
||||
|
||||
// Count for the number of readers which have called |OnReadMetadataCompleted|.
|
||||
// Initialised to 0; incremented for every decoder which has |Load| called;
|
||||
// and decremented for every call to |OnReadMetadataCompleted|. When it is
|
||||
// zero again, all metadata has been read for audio or video, and data bytes
|
||||
// can be downloaded.
|
||||
uint32_t mAudioMetadataReadCount;
|
||||
uint32_t mVideoMetadataReadCount;
|
||||
|
||||
// Array records the index of the decoder/Representation which loaded each
|
||||
// subsegment.
|
||||
nsTArray<int32_t> mVideoSubsegmentLoads;
|
||||
|
||||
// True when Seek is called; will block any downloads until
|
||||
// |NotifySeekInSubsegment| is called, which will set it to false, and will
|
||||
// start a new series of downloads from the seeked subsegment.
|
||||
bool mSeeking;
|
||||
|
||||
// Mutex for statistics.
|
||||
Mutex mStatisticsLock;
|
||||
// Stores snapshot statistics, such as download rate, for the audio|video
|
||||
// data streams. |mStatisticsLock| must be locked for access.
|
||||
nsRefPtr<MediaChannelStatistics> mAudioStatistics;
|
||||
nsRefPtr<MediaChannelStatistics> mVideoStatistics;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,674 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
|
||||
/* This Source Code Form Is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* DASH - Dynamic Adaptive Streaming over HTTP
|
||||
*
|
||||
* DASH is an adaptive bitrate streaming technology where a multimedia file is
|
||||
* partitioned into one or more segments and delivered to a client using HTTP.
|
||||
*
|
||||
* see DASHDecoder.cpp for info on DASH interaction with the media engine.*/
|
||||
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "VideoFrameContainer.h"
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "DASHReader.h"
|
||||
#include "DASHDecoder.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* gDASHReaderLog;
|
||||
#define LOG(msg, ...) PR_LOG(gDASHReaderLog, PR_LOG_DEBUG, \
|
||||
("%p [DASHReader] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) PR_LOG(gDASHReaderLog, PR_LOG_DEBUG, \
|
||||
("%p [DASHReader] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
DASHReader::DASHReader(AbstractMediaDecoder* aDecoder) :
|
||||
MediaDecoderReader(aDecoder),
|
||||
mReadMetadataMonitor("media.dashreader.readmetadata"),
|
||||
mReadyToReadMetadata(false),
|
||||
mDecoderIsShuttingDown(false),
|
||||
mAudioReader(this),
|
||||
mVideoReader(this),
|
||||
mAudioReaders(this),
|
||||
mVideoReaders(this),
|
||||
mSwitchVideoReaders(false),
|
||||
mSwitchCount(-1)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DASHReader);
|
||||
#ifdef PR_LOGGING
|
||||
if (!gDASHReaderLog) {
|
||||
gDASHReaderLog = PR_NewLogModule("DASHReader");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DASHReader::~DASHReader()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DASHReader);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::ResetDecode()
|
||||
{
|
||||
MediaDecoderReader::ResetDecode();
|
||||
nsresult rv;
|
||||
for (uint i = 0; i < mAudioReaders.Length(); i++) {
|
||||
rv = mAudioReaders[i]->ResetDecode();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
for (uint i = 0; i < mVideoReaders.Length(); i++) {
|
||||
rv = mVideoReaders[i]->ResetDecode();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(mAudioReaders.Length() != 0 && mVideoReaders.Length() != 0,
|
||||
"Audio and video readers should exist already.");
|
||||
|
||||
nsresult rv;
|
||||
for (uint i = 0; i < mAudioReaders.Length(); i++) {
|
||||
rv = mAudioReaders[i]->Init(nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
for (uint i = 0; i < mVideoReaders.Length(); i++) {
|
||||
rv = mVideoReaders[i]->Init(nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::AddAudioReader(DASHRepReader* aAudioReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE_VOID(aAudioReader);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
mAudioReaders.AppendElement(aAudioReader);
|
||||
// XXX For now, just pick the first reader to be default.
|
||||
if (!mAudioReader)
|
||||
mAudioReader = aAudioReader;
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::AddVideoReader(DASHRepReader* aVideoReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE_VOID(aVideoReader);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
mVideoReaders.AppendElement(aVideoReader);
|
||||
// XXX For now, just pick the first reader to be default.
|
||||
if (!mVideoReader)
|
||||
mVideoReader = aVideoReader;
|
||||
}
|
||||
|
||||
bool
|
||||
DASHReader::HasAudio()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
return mAudioReader ? mAudioReader->HasAudio() : false;
|
||||
}
|
||||
|
||||
bool
|
||||
DASHReader::HasVideo()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
return mVideoReader ? mVideoReader->HasVideo() : false;
|
||||
}
|
||||
|
||||
int64_t
|
||||
DASHReader::VideoQueueMemoryInUse()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
return VideoQueueMemoryInUse();
|
||||
}
|
||||
|
||||
int64_t
|
||||
DASHReader::AudioQueueMemoryInUse()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
return AudioQueueMemoryInUse();
|
||||
}
|
||||
|
||||
bool
|
||||
DASHReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
if (mVideoReader) {
|
||||
return mVideoReader->DecodeVideoFrame(aKeyframeSkip, aTimeThreshold);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DASHReader::DecodeAudioData()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
return (mAudioReader ? mAudioReader->DecodeAudioData() : false);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// Wait for MPD to be parsed and child readers created.
|
||||
LOG1("Waiting for metadata download.");
|
||||
nsresult rv = WaitForMetadata();
|
||||
// If we get an abort, return silently; the decoder is shutting down.
|
||||
if (NS_ERROR_ABORT == rv) {
|
||||
return NS_OK;
|
||||
}
|
||||
// Verify no other errors before continuing.
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(aTags, "Called with null MetadataTags**.");
|
||||
*aTags = nullptr;
|
||||
|
||||
// Get metadata from child readers.
|
||||
MediaInfo audioInfo, videoInfo;
|
||||
|
||||
// Read metadata for all video streams.
|
||||
for (uint i = 0; i < mVideoReaders.Length(); i++) {
|
||||
// Use an nsAutoPtr here to ensure |tags| memory does not leak.
|
||||
nsAutoPtr<HTMLMediaElement::MetadataTags> tags;
|
||||
rv = mVideoReaders[i]->ReadMetadata(&videoInfo, getter_Transfers(tags));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Use metadata from current video sub reader to populate aInfo.
|
||||
if (mVideoReaders[i] == mVideoReader) {
|
||||
mInfo.mVideo = videoInfo.mVideo;
|
||||
}
|
||||
}
|
||||
// Read metadata for audio stream.
|
||||
// Note: Getting metadata tags from audio reader only for now.
|
||||
// XXX Audio stream switching not yet supported.
|
||||
if (mAudioReader) {
|
||||
rv = mAudioReader->ReadMetadata(&audioInfo, aTags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mInfo.mAudio = audioInfo.mAudio;
|
||||
}
|
||||
|
||||
*aInfo = mInfo;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::Seek(int64_t aTime,
|
||||
int64_t aStartTime,
|
||||
int64_t aEndTime,
|
||||
int64_t aCurrentTime)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
NS_ENSURE_SUCCESS(ResetDecode(), NS_ERROR_FAILURE);
|
||||
|
||||
LOG("Seeking to [%.2fs]", aTime/1000000.0);
|
||||
|
||||
nsresult rv;
|
||||
DASHDecoder* dashDecoder = static_cast<DASHDecoder*>(mDecoder);
|
||||
|
||||
if (mAudioReader) {
|
||||
int64_t subsegmentIdx = -1;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
subsegmentIdx = mAudioReader->GetSubsegmentForSeekTime(aTime);
|
||||
NS_ENSURE_TRUE(0 <= subsegmentIdx, NS_ERROR_ILLEGAL_VALUE);
|
||||
}
|
||||
dashDecoder->NotifySeekInAudioSubsegment(subsegmentIdx);
|
||||
|
||||
rv = mAudioReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (mVideoReader) {
|
||||
// Determine the video subsegment we're seeking to.
|
||||
int32_t subsegmentIdx = -1;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
subsegmentIdx = mVideoReader->GetSubsegmentForSeekTime(aTime);
|
||||
NS_ENSURE_TRUE(0 <= subsegmentIdx, NS_ERROR_ILLEGAL_VALUE);
|
||||
}
|
||||
|
||||
LOG("Seek to [%.2fs] found in video subsegment [%d]",
|
||||
aTime/1000000.0, subsegmentIdx);
|
||||
|
||||
// Determine if/which video reader previously downloaded this subsegment.
|
||||
int32_t readerIdx = dashDecoder->GetRepIdxForVideoSubsegmentLoad(subsegmentIdx);
|
||||
|
||||
dashDecoder->NotifySeekInVideoSubsegment(readerIdx, subsegmentIdx);
|
||||
|
||||
if (0 <= readerIdx) {
|
||||
NS_ENSURE_TRUE(readerIdx < mVideoReaders.Length(),
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
// Switch to this reader and do the Seek.
|
||||
DASHRepReader* fromReader = mVideoReader;
|
||||
DASHRepReader* toReader = mVideoReaders[readerIdx];
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (fromReader != toReader) {
|
||||
LOG("Switching video readers now from [%p] to [%p] for a seek to "
|
||||
"[%.2fs] in subsegment [%d]",
|
||||
fromReader, toReader, aTime/1000000.0, subsegmentIdx);
|
||||
|
||||
mVideoReader = toReader;
|
||||
}
|
||||
}
|
||||
|
||||
rv = mVideoReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Go back to the appropriate count in the switching history, and setup
|
||||
// this main reader and the sub readers for the next switch (if any).
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mSwitchCount = dashDecoder->GetSwitchCountAtVideoSubsegment(subsegmentIdx);
|
||||
LOG("After mVideoReader->Seek() mSwitchCount %d", mSwitchCount);
|
||||
NS_ENSURE_TRUE(0 <= mSwitchCount, NS_ERROR_ILLEGAL_VALUE);
|
||||
NS_ENSURE_TRUE(mSwitchCount <= subsegmentIdx, NS_ERROR_ILLEGAL_VALUE);
|
||||
}
|
||||
} else {
|
||||
LOG("Error getting rep idx for video subsegment [%d]",
|
||||
subsegmentIdx);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHReader::GetBuffered(TimeRanges* aBuffered,
|
||||
int64_t aStartTime)
|
||||
{
|
||||
NS_ENSURE_ARG(aBuffered);
|
||||
|
||||
MediaResource* resource = nullptr;
|
||||
AbstractMediaDecoder* decoder = nullptr;
|
||||
|
||||
TimeRanges audioBuffered, videoBuffered;
|
||||
uint32_t audioRangeCount = 0, videoRangeCount = 0;
|
||||
bool audioCachedAtEnd = false, videoCachedAtEnd = false;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Get all audio and video buffered ranges. Include inactive streams, since
|
||||
// we may have carried out a seek and future subsegments may be in currently
|
||||
// inactive decoders.
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
for (uint32_t i = 0; i < mAudioReaders.Length(); i++) {
|
||||
decoder = mAudioReaders[i]->GetDecoder();
|
||||
NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
|
||||
resource = decoder->GetResource();
|
||||
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
|
||||
resource->Pin();
|
||||
rv = mAudioReaders[i]->GetBuffered(&audioBuffered, aStartTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// If data was cached at the end, then the final timestamp refers to the
|
||||
// end of the data. Use this later to extend end time if necessary.
|
||||
if (!audioCachedAtEnd) {
|
||||
audioCachedAtEnd = mAudioReaders[i]->IsDataCachedAtEndOfSubsegments();
|
||||
}
|
||||
resource->Unpin();
|
||||
}
|
||||
for (uint32_t i = 0; i < mVideoReaders.Length(); i++) {
|
||||
decoder = mVideoReaders[i]->GetDecoder();
|
||||
NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
|
||||
resource = decoder->GetResource();
|
||||
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
|
||||
resource->Pin();
|
||||
rv = mVideoReaders[i]->GetBuffered(&videoBuffered, aStartTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// If data was cached at the end, then the final timestamp refers to the
|
||||
// end of the data. Use this later to extend end time if necessary.
|
||||
if (!videoCachedAtEnd) {
|
||||
videoCachedAtEnd = mVideoReaders[i]->IsDataCachedAtEndOfSubsegments();
|
||||
}
|
||||
resource->Unpin();
|
||||
}
|
||||
|
||||
audioBuffered.Normalize();
|
||||
videoBuffered.Normalize();
|
||||
|
||||
rv = audioBuffered.GetLength(&audioRangeCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = videoBuffered.GetLength(&videoRangeCount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
double start = 0, end = 0;
|
||||
for (uint32_t i = 0; i < audioRangeCount; i++) {
|
||||
rv = audioBuffered.Start(i, &start);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = audioBuffered.End(i, &end);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
LOG("audioBuffered[%d] = (%f, %f)",
|
||||
i, start, end);
|
||||
}
|
||||
for (uint32_t i = 0; i < videoRangeCount; i++) {
|
||||
rv = videoBuffered.Start(i, &start);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = videoBuffered.End(i, &end);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
LOG("videoBuffered[%d] = (%f, %f)",
|
||||
i, start, end);
|
||||
}
|
||||
#endif
|
||||
|
||||
// If audio and video are cached to the end of their subsegments, extend the
|
||||
// end time of the shorter of the two. Presentation of the shorter stream
|
||||
// will stop at the end, while the other continues until the combined
|
||||
// playback is complete.
|
||||
// Note: Only in cases where the shorter stream is fully cached, and the
|
||||
// longer stream is partially cached, but with more time buffered than the
|
||||
// shorter stream.
|
||||
//
|
||||
// Audio ========|
|
||||
// 20
|
||||
// Video ============|----|
|
||||
// 30 40
|
||||
// Combo ============| <----- End time EXTENDED.
|
||||
//
|
||||
// For example, audio is fully cached to 20s, but video is partially cached
|
||||
// to 30s, full duration 40s. In this case, the buffered end time should be
|
||||
// extended to the video's end time.
|
||||
//
|
||||
// Audio =================|
|
||||
// 40
|
||||
// Video ========|----|
|
||||
// 20 30
|
||||
// Combo ========| <------ End time NOT EXTENDED.
|
||||
//
|
||||
// Conversely, if the longer stream is fully cached, but the shorter one is
|
||||
// not, no extension of end time should occur - we should consider the
|
||||
// partially cached, shorter end time to be the end time of the combined
|
||||
// stream
|
||||
|
||||
if (audioCachedAtEnd || videoCachedAtEnd) {
|
||||
NS_ENSURE_TRUE(audioRangeCount, NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(videoRangeCount, NS_ERROR_FAILURE);
|
||||
|
||||
double audioEndTime = 0, videoEndTime = 0;
|
||||
// Get end time of the last range of buffered audio.
|
||||
audioEndTime = audioBuffered.GetFinalEndTime();
|
||||
NS_ENSURE_TRUE(audioEndTime > 0, NS_ERROR_ILLEGAL_VALUE);
|
||||
// Get end time of the last range of buffered video.
|
||||
videoEndTime = videoBuffered.GetFinalEndTime();
|
||||
NS_ENSURE_TRUE(videoEndTime > 0, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// API for TimeRanges requires extending through adding and normalizing.
|
||||
if (videoCachedAtEnd && audioEndTime > videoEndTime) {
|
||||
videoBuffered.Add(videoEndTime, audioEndTime);
|
||||
videoBuffered.Normalize();
|
||||
LOG("videoBuffered extended to %f", audioEndTime);
|
||||
} else if (audioCachedAtEnd && videoEndTime > audioEndTime) {
|
||||
audioBuffered.Add(audioEndTime, videoEndTime);
|
||||
audioBuffered.Normalize();
|
||||
LOG("audioBuffered extended to %f", videoEndTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate intersecting ranges for video and audio.
|
||||
if (!mAudioReaders.IsEmpty() && !mVideoReaders.IsEmpty()) {
|
||||
for (uint32_t i = 0; i < audioRangeCount; i++) {
|
||||
// |A|udio, |V|ideo, |I|ntersect.
|
||||
double startA, startV, startI;
|
||||
double endA, endV, endI;
|
||||
rv = audioBuffered.Start(i, &startA);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = audioBuffered.End(i, &endA);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (uint32_t j = 0; j < videoRangeCount; j++) {
|
||||
rv = videoBuffered.Start(i, &startV);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = videoBuffered.End(i, &endV);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If video block is before audio block, compare next video block.
|
||||
if (startA > endV) {
|
||||
continue;
|
||||
// If video block is after audio block, all of them are; compare next
|
||||
// audio block.
|
||||
} else if (endA < startV) {
|
||||
break;
|
||||
}
|
||||
// Calculate intersections of current audio and video blocks.
|
||||
startI = (startA > startV) ? startA : startV;
|
||||
endI = (endA > endV) ? endV : endA;
|
||||
aBuffered->Add(startI, endI);
|
||||
}
|
||||
}
|
||||
} else if (!mAudioReaders.IsEmpty()) {
|
||||
*aBuffered = audioBuffered;
|
||||
} else if (!mVideoReaders.IsEmpty()) {
|
||||
*aBuffered = videoBuffered;
|
||||
} else {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
VideoData*
|
||||
DASHReader::FindStartTime(int64_t& aOutStartTime)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
|
||||
"Should be on state machine or decode thread.");
|
||||
|
||||
// Extract the start times of the bitstreams in order to calculate
|
||||
// the duration.
|
||||
int64_t videoStartTime = INT64_MAX;
|
||||
int64_t audioStartTime = INT64_MAX;
|
||||
VideoData* videoData = nullptr;
|
||||
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
if (HasVideo()) {
|
||||
// Forward to video reader.
|
||||
videoData = mVideoReader->DecodeToFirstVideoData();
|
||||
if (videoData) {
|
||||
videoStartTime = videoData->mTime;
|
||||
}
|
||||
}
|
||||
if (HasAudio()) {
|
||||
// Forward to audio reader.
|
||||
AudioData* audioData = mAudioReader->DecodeToFirstAudioData();
|
||||
if (audioData) {
|
||||
audioStartTime = audioData->mTime;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t startTime = std::min(videoStartTime, audioStartTime);
|
||||
if (startTime != INT64_MAX) {
|
||||
aOutStartTime = startTime;
|
||||
}
|
||||
|
||||
return videoData;
|
||||
}
|
||||
|
||||
MediaQueue<AudioData>&
|
||||
DASHReader::AudioQueue()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(mAudioReader, "mAudioReader is NULL!");
|
||||
return mAudioQueue;
|
||||
}
|
||||
|
||||
MediaQueue<VideoData>&
|
||||
DASHReader::VideoQueue()
|
||||
{
|
||||
ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
|
||||
mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(mVideoReader, "mVideoReader is NULL!");
|
||||
return mVideoQueue;
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::RequestVideoReaderSwitch(uint32_t aFromReaderIdx,
|
||||
uint32_t aToReaderIdx,
|
||||
uint32_t aSubsegmentIdx)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ASSERTION(aFromReaderIdx < mVideoReaders.Length(),
|
||||
"From index is greater than number of video readers!");
|
||||
NS_ASSERTION(aToReaderIdx < mVideoReaders.Length(),
|
||||
"To index is greater than number of video readers!");
|
||||
NS_ASSERTION(aToReaderIdx != aFromReaderIdx,
|
||||
"Don't request switches to same reader!");
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
if (mSwitchCount < 0) {
|
||||
mSwitchCount = 0;
|
||||
}
|
||||
|
||||
DASHRepReader* fromReader = mVideoReaders[aFromReaderIdx];
|
||||
DASHRepReader* toReader = mVideoReaders[aToReaderIdx];
|
||||
|
||||
LOG("Switch requested from reader [%d] [%p] to reader [%d] [%p] "
|
||||
"at subsegment[%d].",
|
||||
aFromReaderIdx, fromReader, aToReaderIdx, toReader, aSubsegmentIdx);
|
||||
|
||||
// Append the subsegment index to the list of pending switches.
|
||||
for (uint32_t i = 0; i < mSwitchToVideoSubsegmentIndexes.Length(); i++) {
|
||||
if (mSwitchToVideoSubsegmentIndexes[i] == aSubsegmentIdx) {
|
||||
// A backwards |Seek| has changed the switching history; delete from
|
||||
// this point on.
|
||||
mSwitchToVideoSubsegmentIndexes.TruncateLength(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mSwitchToVideoSubsegmentIndexes.AppendElement(aSubsegmentIdx);
|
||||
|
||||
// Tell the SWITCH FROM reader when it should stop reading.
|
||||
fromReader->RequestSwitchAtSubsegment(aSubsegmentIdx, toReader);
|
||||
|
||||
// Tell the SWITCH TO reader to seek to the correct offset.
|
||||
toReader->RequestSeekToSubsegment(aSubsegmentIdx);
|
||||
|
||||
mSwitchVideoReaders = true;
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::PossiblySwitchVideoReaders()
|
||||
{
|
||||
NS_ASSERTION(mDecoder, "Decoder should not be null");
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// Flag to switch streams is set in |RequestVideoReaderSwitch|.
|
||||
if (!mSwitchVideoReaders) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only switch if we reached a switch access point.
|
||||
NS_ENSURE_TRUE_VOID(0 <= mSwitchCount);
|
||||
NS_ENSURE_TRUE_VOID((uint32_t)mSwitchCount < mSwitchToVideoSubsegmentIndexes.Length());
|
||||
uint32_t switchIdx = mSwitchToVideoSubsegmentIndexes[mSwitchCount];
|
||||
if (!mVideoReader->HasReachedSubsegment(switchIdx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Representation index to switch to.
|
||||
DASHDecoder* dashDecoder = static_cast<DASHDecoder*>(mDecoder);
|
||||
int32_t toReaderIdx = dashDecoder->GetRepIdxForVideoSubsegmentLoad(switchIdx);
|
||||
NS_ENSURE_TRUE_VOID(0 <= toReaderIdx);
|
||||
NS_ENSURE_TRUE_VOID((uint32_t)toReaderIdx < mVideoReaders.Length());
|
||||
|
||||
DASHRepReader* fromReader = mVideoReader;
|
||||
DASHRepReader* toReader = mVideoReaders[toReaderIdx];
|
||||
NS_ENSURE_TRUE_VOID(fromReader != toReader);
|
||||
|
||||
LOG("Switching video readers now from [%p] to [%p] at subsegment [%d]: "
|
||||
"mSwitchCount [%d].",
|
||||
fromReader, toReader, switchIdx, mSwitchCount);
|
||||
|
||||
// Switch readers while in the monitor.
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mVideoReader = toReader;
|
||||
|
||||
// Prep readers for next switch, also while in monitor.
|
||||
if ((uint32_t)++mSwitchCount < mSwitchToVideoSubsegmentIndexes.Length()) {
|
||||
// Get the subsegment at which to switch.
|
||||
switchIdx = mSwitchToVideoSubsegmentIndexes[mSwitchCount];
|
||||
|
||||
// Update from and to reader ptrs for next switch.
|
||||
fromReader = toReader;
|
||||
toReaderIdx = dashDecoder->GetRepIdxForVideoSubsegmentLoad(switchIdx);
|
||||
toReader = mVideoReaders[toReaderIdx];
|
||||
NS_ENSURE_TRUE_VOID((uint32_t)toReaderIdx < mVideoReaders.Length());
|
||||
NS_ENSURE_TRUE_VOID(fromReader != toReader);
|
||||
|
||||
// Tell the SWITCH FROM reader when it should stop reading.
|
||||
fromReader->RequestSwitchAtSubsegment(switchIdx, toReader);
|
||||
|
||||
// Tell the SWITCH TO reader to seek to the correct offset.
|
||||
toReader->RequestSeekToSubsegment(switchIdx);
|
||||
} else {
|
||||
// If there are no more pending switches, unset the switch readers flag.
|
||||
mSwitchVideoReaders = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHReader::PrepareToDecode()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// Flag to switch streams is set by |DASHDecoder|.
|
||||
if (!mSwitchVideoReaders) {
|
||||
return;
|
||||
}
|
||||
|
||||
PossiblySwitchVideoReaders();
|
||||
|
||||
// Prepare each sub reader for decoding: includes seeking to the correct
|
||||
// offset if a seek was previously requested.
|
||||
for (uint32_t i = 0; i < mVideoReaders.Length(); i++) {
|
||||
mVideoReaders[i]->PrepareToDecode();
|
||||
}
|
||||
}
|
||||
|
||||
DASHRepReader*
|
||||
DASHReader::GetReaderForSubsegment(uint32_t aSubsegmentIdx)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
DASHDecoder* dashDecoder = static_cast<DASHDecoder*>(mDecoder);
|
||||
int32_t repIdx =
|
||||
dashDecoder->GetRepIdxForVideoSubsegmentLoadAfterSeek((int32_t)aSubsegmentIdx);
|
||||
if (0 <= repIdx && repIdx < mVideoReaders.Length()) {
|
||||
return mVideoReaders[repIdx];
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace mozilla
|
@ -1,303 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
|
||||
/* This Source Code Form Is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* DASH - Dynamic Adaptive Streaming over HTTP
|
||||
*
|
||||
* DASH is an adaptive bitrate streaming technology where a multimedia file is
|
||||
* partitioned into one or more segments and delivered to a client using HTTP.
|
||||
*
|
||||
* see DASHDecoder.cpp for comments on DASH object interaction
|
||||
*/
|
||||
|
||||
#if !defined(DASHReader_h_)
|
||||
#define DASHReader_h_
|
||||
|
||||
#include "VideoUtils.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "DASHRepReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DASHRepReader;
|
||||
|
||||
class DASHReader : public MediaDecoderReader
|
||||
{
|
||||
public:
|
||||
DASHReader(AbstractMediaDecoder* aDecoder);
|
||||
~DASHReader();
|
||||
nsresult ResetDecode() MOZ_OVERRIDE;
|
||||
|
||||
// Adds a pointer to a audio/video reader for a media |Representation|.
|
||||
// Called on the main thread only.
|
||||
void AddAudioReader(DASHRepReader* aAudioReader);
|
||||
void AddVideoReader(DASHRepReader* aVideoReader);
|
||||
|
||||
// Waits for metadata bytes to be downloaded, then reads and parses them.
|
||||
// Called on the decode thread only.
|
||||
nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) MOZ_OVERRIDE;
|
||||
|
||||
// Waits for |ReadyToReadMetadata| or |NotifyDecoderShuttingDown|
|
||||
// notification, whichever comes first. Ensures no attempt to read metadata
|
||||
// during |DASHDecoder|::|Shutdown|. Called on decode thread only.
|
||||
nsresult WaitForMetadata() {
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
ReentrantMonitorAutoEnter mon(mReadMetadataMonitor);
|
||||
while (true) {
|
||||
// Abort if the decoder has started shutting down.
|
||||
if (mDecoderIsShuttingDown) {
|
||||
return NS_ERROR_ABORT;
|
||||
} else if (mReadyToReadMetadata) {
|
||||
break;
|
||||
}
|
||||
mon.Wait();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Called on the main thread by |DASHDecoder| to notify that metadata bytes
|
||||
// have been downloaded.
|
||||
void ReadyToReadMetadata() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
ReentrantMonitorAutoEnter mon(mReadMetadataMonitor);
|
||||
mReadyToReadMetadata = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
// Called on the main thread by |DASHDecoder| when it starts Shutdown. Will
|
||||
// wake metadata monitor if waiting for a silent return from |ReadMetadata|.
|
||||
void NotifyDecoderShuttingDown() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
ReentrantMonitorAutoEnter metadataMon(mReadMetadataMonitor);
|
||||
mDecoderIsShuttingDown = true;
|
||||
// Notify |ReadMetadata| of the shutdown if it's waiting.
|
||||
metadataMon.NotifyAll();
|
||||
}
|
||||
|
||||
// Audio/video status are dependent on the presence of audio/video readers.
|
||||
// Call on decode thread only.
|
||||
bool HasAudio() MOZ_OVERRIDE;
|
||||
bool HasVideo() MOZ_OVERRIDE;
|
||||
|
||||
// Returns references to the audio/video queues of sub-readers. Called on
|
||||
// decode, state machine and audio threads.
|
||||
MediaQueue<AudioData>& AudioQueue() MOZ_OVERRIDE;
|
||||
MediaQueue<VideoData>& VideoQueue() MOZ_OVERRIDE;
|
||||
|
||||
// Called from MediaDecoderStateMachine on the main thread.
|
||||
nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
|
||||
|
||||
// Used by |MediaMemoryReporter|.
|
||||
int64_t VideoQueueMemoryInUse() MOZ_OVERRIDE;
|
||||
int64_t AudioQueueMemoryInUse() MOZ_OVERRIDE;
|
||||
|
||||
// Called on the decode thread, at the start of the decode loop, before
|
||||
// |DecodeVideoFrame|. Carries out video reader switch if previously
|
||||
// requested, and tells sub-readers to |PrepareToDecode|.
|
||||
void PrepareToDecode() MOZ_OVERRIDE;
|
||||
|
||||
// Called on the decode thread.
|
||||
bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
bool DecodeAudioData() MOZ_OVERRIDE;
|
||||
|
||||
// Converts seek time to byte offset. Called on the decode thread only.
|
||||
nsresult Seek(int64_t aTime,
|
||||
int64_t aStartTime,
|
||||
int64_t aEndTime,
|
||||
int64_t aCurrentTime) MOZ_OVERRIDE;
|
||||
|
||||
// Called by state machine on multiple threads.
|
||||
nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE;
|
||||
|
||||
// Called on the state machine or decode threads.
|
||||
VideoData* FindStartTime(int64_t& aOutStartTime) MOZ_OVERRIDE;
|
||||
|
||||
// Prepares for an upcoming switch of video readers. Called by
|
||||
// |DASHDecoder| when it has switched download streams. Sets the index of
|
||||
// the reader to switch TO and the index of the subsegment to switch AT
|
||||
// (start offset). (Note: Subsegment boundaries are switch access points for
|
||||
// DASH-WebM). Called on the main thread. Must be in the decode monitor.
|
||||
void RequestVideoReaderSwitch(uint32_t aFromReaderIdx,
|
||||
uint32_t aToReaderIdx,
|
||||
uint32_t aSubsegmentIdx);
|
||||
|
||||
// Returns a pointer to the reader which should be used for the specified
|
||||
// subsegment. Called on the decode thread only.
|
||||
DASHRepReader* GetReaderForSubsegment(uint32_t aSubsegmentIdx);
|
||||
|
||||
private:
|
||||
// Switches video subreaders if a stream-switch flag has been set, and the
|
||||
// current reader has read up to the switching subsegment (start offset).
|
||||
// Called on the decode thread only.
|
||||
void PossiblySwitchVideoReaders();
|
||||
|
||||
// Monitor and booleans used to wait for metadata bytes to be downloaded, and
|
||||
// skip reading metadata if |DASHDecoder|'s shutdown is in progress.
|
||||
ReentrantMonitor mReadMetadataMonitor;
|
||||
bool mReadyToReadMetadata;
|
||||
bool mDecoderIsShuttingDown;
|
||||
|
||||
// Wrapper class protecting accesses to sub-readers. Asserts that the
|
||||
// decoder monitor has been entered for write access on all threads and read
|
||||
// access on all threads that are not the decode thread. Read access on the
|
||||
// decode thread does not need to be protected.
|
||||
class MonitoredSubReader
|
||||
{
|
||||
public:
|
||||
// Main constructor takes a pointer to the owning |DASHReader| to verify
|
||||
// correct entry into the decoder's |ReentrantMonitor|.
|
||||
MonitoredSubReader(DASHReader* aReader) :
|
||||
mReader(aReader),
|
||||
mSubReader(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DASHReader::MonitoredSubReader);
|
||||
NS_ASSERTION(mReader, "Reader is null!");
|
||||
}
|
||||
// Note: |mSubReader|'s refcount will be decremented in this destructor.
|
||||
~MonitoredSubReader()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DASHReader::MonitoredSubReader);
|
||||
}
|
||||
|
||||
// Override '=' to always assert thread is "in monitor" for writes/changes
|
||||
// to |mSubReader|.
|
||||
MonitoredSubReader& operator=(DASHRepReader* rhs)
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
mSubReader = rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Override '*' to assert threads other than the decode thread are "in
|
||||
// monitor" for ptr reads.
|
||||
operator DASHRepReader*() const
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
if (!mReader->GetDecoder()->OnDecodeThread()) {
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
}
|
||||
return mSubReader;
|
||||
}
|
||||
|
||||
// Override '->' to assert threads other than the decode thread are "in
|
||||
// monitor" for |mSubReader| function calls.
|
||||
DASHRepReader* operator->() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
// Pointer to |DASHReader| object which owns this |MonitoredSubReader|.
|
||||
DASHReader* mReader;
|
||||
// Ref ptr to the sub reader.
|
||||
nsRefPtr<DASHRepReader> mSubReader;
|
||||
};
|
||||
|
||||
// Wrapped ref ptrs to current sub-readers of individual media
|
||||
// |Representation|s. Decoder monitor must be entered for write access on all
|
||||
// threads and read access on all threads that are not the decode thread.
|
||||
// Read access on the decode thread does not need to be protected.
|
||||
// Note: |MonitoredSubReader| class will assert correct monitor use.
|
||||
MonitoredSubReader mAudioReader;
|
||||
MonitoredSubReader mVideoReader;
|
||||
|
||||
// Wrapper class protecting accesses to sub-reader list. Asserts that the
|
||||
// decoder monitor has been entered for write access on all threads and read
|
||||
// access on all threads that are not the decode thread. Read access on the
|
||||
// decode thread does not need to be protected.
|
||||
// Note: Elems accessed via operator[] are not protected with monitor
|
||||
// assertion checks once obtained.
|
||||
class MonitoredSubReaderList
|
||||
{
|
||||
public:
|
||||
// Main constructor takes a pointer to the owning |DASHReader| to verify
|
||||
// correct entry into the decoder's |ReentrantMonitor|.
|
||||
MonitoredSubReaderList(DASHReader* aReader) :
|
||||
mReader(aReader)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DASHReader::MonitoredSubReaderList);
|
||||
NS_ASSERTION(mReader, "Reader is null!");
|
||||
}
|
||||
// Note: Elements in |mSubReaderList| will have their refcounts decremented
|
||||
// in this destructor.
|
||||
~MonitoredSubReaderList()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DASHReader::MonitoredSubReaderList);
|
||||
}
|
||||
|
||||
// Returns Length of |mSubReaderList| array. Will assert threads other than
|
||||
// the decode thread are "in monitor".
|
||||
uint32_t Length() const
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
if (!mReader->GetDecoder()->OnDecodeThread()) {
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
}
|
||||
return mSubReaderList.Length();
|
||||
}
|
||||
|
||||
// Returns true if |mSubReaderList| is empty. Will assert that threads
|
||||
// other than the decode thread are "in monitor".
|
||||
bool IsEmpty() const
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
if (!mReader->GetDecoder()->OnDecodeThread()) {
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
}
|
||||
return mSubReaderList.IsEmpty();
|
||||
}
|
||||
// Override '[]' to assert threads other than the decode thread are "in
|
||||
// monitor" for accessing individual elems. Note: elems returned do not
|
||||
// have monitor assertions builtin like |MonitoredSubReader| objects.
|
||||
nsRefPtr<DASHRepReader>& operator[](uint32_t i)
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
if (!mReader->GetDecoder()->OnDecodeThread()) {
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
}
|
||||
return mSubReaderList[i];
|
||||
}
|
||||
|
||||
// Appends a reader to the end of |mSubReaderList|. Will always assert that
|
||||
// the thread is "in monitor".
|
||||
void
|
||||
AppendElement(DASHRepReader* aReader)
|
||||
{
|
||||
NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
|
||||
mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
mSubReaderList.AppendElement(aReader);
|
||||
}
|
||||
private:
|
||||
// Pointer to |DASHReader| object which owns this |MonitoredSubReader|.
|
||||
DASHReader* mReader;
|
||||
// Ref ptrs to the sub readers.
|
||||
nsTArray<nsRefPtr<DASHRepReader> > mSubReaderList;
|
||||
};
|
||||
|
||||
// Ref ptrs to all sub-readers of individual media |Representation|s.
|
||||
// Decoder monitor must be entered for write access on all threads and read
|
||||
// access on all threads that are not the decode thread. Read acces on the
|
||||
// decode thread does not need to be protected.
|
||||
MonitoredSubReaderList mAudioReaders;
|
||||
MonitoredSubReaderList mVideoReaders;
|
||||
|
||||
// When true, indicates that we should switch reader. Must be in the monitor
|
||||
// for write access and read access off the decode thread.
|
||||
bool mSwitchVideoReaders;
|
||||
|
||||
// Indicates the subsegment index at which the reader should switch. Must be
|
||||
// in the monitor for write access and read access off the decode thread.
|
||||
nsTArray<uint32_t> mSwitchToVideoSubsegmentIndexes;
|
||||
|
||||
// Counts the number of switches that have taken place. Must be in the
|
||||
// monitor for write access and read access off the decode thread.
|
||||
int32_t mSwitchCount;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,517 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
|
||||
/* This Source Code Form Is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* DASH - Dynamic Adaptive Streaming over HTTP
|
||||
*
|
||||
* DASH is an adaptive bitrate streaming technology where a multimedia file is
|
||||
* partitioned into one or more segments and delivered to a client using HTTP.
|
||||
*
|
||||
* see DASHDecoder.cpp for info on DASH interaction with the media engine.*/
|
||||
|
||||
#include "prlog.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "SegmentBase.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "DASHReader.h"
|
||||
#include "MediaResource.h"
|
||||
#include "DASHRepDecoder.h"
|
||||
#include "WebMReader.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define LOG(msg, ...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [DASHRepDecoder] " msg, this, __VA_ARGS__))
|
||||
#define LOG1(msg) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, \
|
||||
("%p [DASHRepDecoder] " msg, this))
|
||||
#else
|
||||
#define LOG(msg, ...)
|
||||
#define LOG1(msg)
|
||||
#endif
|
||||
|
||||
MediaDecoderStateMachine*
|
||||
DASHRepDecoder::CreateStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
// Do not create; just return current state machine.
|
||||
return mDecoderStateMachine;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHRepDecoder::SetStateMachine(MediaDecoderStateMachine* aSM)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mDecoderStateMachine = aSM;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetResource(MediaResource* aResource)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mResource = aResource;
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetMPDRepresentation(Representation const * aRep)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mMPDRepresentation = aRep;
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetReader(WebMReader* aReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
mReader = aReader;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHRepDecoder::Load(nsIStreamListener** aListener,
|
||||
MediaDecoder* aCloneDonor)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ENSURE_TRUE(mMPDRepresentation, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Get init range and index range from MPD.
|
||||
SegmentBase const * segmentBase = mMPDRepresentation->GetSegmentBase();
|
||||
NS_ENSURE_TRUE(segmentBase, NS_ERROR_NULL_POINTER);
|
||||
|
||||
// Get and set init range.
|
||||
segmentBase->GetInitRange(&mInitByteRange.mStart, &mInitByteRange.mEnd);
|
||||
NS_ENSURE_TRUE(!mInitByteRange.IsNull(), NS_ERROR_NOT_INITIALIZED);
|
||||
mReader->SetInitByteRange(mInitByteRange);
|
||||
|
||||
// Get and set index range.
|
||||
segmentBase->GetIndexRange(&mIndexByteRange.mStart, &mIndexByteRange.mEnd);
|
||||
NS_ENSURE_TRUE(!mIndexByteRange.IsNull(), NS_ERROR_NOT_INITIALIZED);
|
||||
mReader->SetIndexByteRange(mIndexByteRange);
|
||||
|
||||
// Determine byte range to Open.
|
||||
// For small deltas between init and index ranges, we need to bundle the byte
|
||||
// range requests together in order to deal with |MediaCache|'s control of
|
||||
// seeking (see |MediaCache|::|Update|). |MediaCache| will not initiate a
|
||||
// |ChannelMediaResource|::|CacheClientSeek| for the INDEX byte range if the
|
||||
// delta between it and the INIT byte ranges is less than
|
||||
// |SEEK_VS_READ_THRESHOLD|. To get around this, request all metadata bytes
|
||||
// now so |MediaCache| can assume the bytes are en route.
|
||||
int64_t delta = std::max(mIndexByteRange.mStart, mInitByteRange.mStart)
|
||||
- std::min(mIndexByteRange.mEnd, mInitByteRange.mEnd);
|
||||
MediaByteRange byteRange;
|
||||
if (delta <= SEEK_VS_READ_THRESHOLD) {
|
||||
byteRange.mStart = std::min(mIndexByteRange.mStart, mInitByteRange.mStart);
|
||||
byteRange.mEnd = std::max(mIndexByteRange.mEnd, mInitByteRange.mEnd);
|
||||
// Loading everything in one chunk .
|
||||
mMetadataChunkCount = 1;
|
||||
} else {
|
||||
byteRange = mInitByteRange;
|
||||
// Loading in two chunks: init and index.
|
||||
mMetadataChunkCount = 2;
|
||||
}
|
||||
mCurrentByteRange = byteRange;
|
||||
return mResource->OpenByteRange(nullptr, byteRange);
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NotifyDownloadEnded(nsresult aStatus)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
if (!mMainDecoder) {
|
||||
if (!mShuttingDown) {
|
||||
LOG("Error! Main Decoder is null before shutdown: mMainDecoder [%p] ",
|
||||
mMainDecoder.get());
|
||||
DecodeError();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
// Decrement counter as metadata chunks are downloaded.
|
||||
// Note: Reader gets next chunk download via |ChannelMediaResource|:|Seek|.
|
||||
if (mMetadataChunkCount > 0) {
|
||||
LOG("Metadata chunk [%d] downloaded: range requested [%lld - %lld] "
|
||||
"subsegmentIdx [%d]",
|
||||
mMetadataChunkCount,
|
||||
mCurrentByteRange.mStart, mCurrentByteRange.mEnd, mSubsegmentIdx);
|
||||
mMetadataChunkCount--;
|
||||
} else {
|
||||
LOG("Byte range downloaded: status [%x] range requested [%lld - %lld] "
|
||||
"subsegmentIdx [%d]",
|
||||
aStatus, mCurrentByteRange.mStart, mCurrentByteRange.mEnd,
|
||||
mSubsegmentIdx);
|
||||
if ((uint32_t)mSubsegmentIdx == mByteRanges.Length()-1) {
|
||||
mResource->NotifyLastByteRange();
|
||||
}
|
||||
// Notify main decoder that a DATA byte range is downloaded.
|
||||
mMainDecoder->NotifyDownloadEnded(this, aStatus, mSubsegmentIdx);
|
||||
}
|
||||
} else if (aStatus == NS_BINDING_ABORTED) {
|
||||
LOG("Media download has been cancelled by the user: aStatus [%x].",
|
||||
aStatus);
|
||||
if (mMainDecoder) {
|
||||
mMainDecoder->LoadAborted();
|
||||
}
|
||||
return;
|
||||
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
|
||||
LOG("Network error trying to download MPD: aStatus [%x].", aStatus);
|
||||
NetworkError();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::OnReadMetadataCompleted()
|
||||
{
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// If shutting down, just return silently.
|
||||
if (mShuttingDown) {
|
||||
LOG1("Shutting down! Ignoring OnReadMetadataCompleted().");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG1("Metadata has been read.");
|
||||
|
||||
// Metadata loaded and read for this stream; ok to populate byte ranges.
|
||||
nsresult rv = PopulateByteRanges();
|
||||
if (NS_FAILED(rv) || mByteRanges.IsEmpty()) {
|
||||
LOG("Error populating byte ranges [%x]", rv);
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
mMainDecoder->OnReadMetadataCompleted(this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHRepDecoder::PopulateByteRanges()
|
||||
{
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// Should not be called during shutdown.
|
||||
NS_ENSURE_FALSE(mShuttingDown, NS_ERROR_UNEXPECTED);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
if (!mByteRanges.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ENSURE_TRUE(mReader, NS_ERROR_NULL_POINTER);
|
||||
LOG1("Populating byte range array.");
|
||||
return mReader->GetSubsegmentByteRanges(mByteRanges);
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::LoadNextByteRange()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
NS_ASSERTION(mResource, "Error: resource is reported as null!");
|
||||
|
||||
// Return silently if shutting down.
|
||||
if (mShuttingDown) {
|
||||
LOG1("Shutting down! Ignoring LoadNextByteRange().");
|
||||
return;
|
||||
}
|
||||
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
NS_ASSERTION(mMainDecoder, "Error: main decoder is null!");
|
||||
NS_ASSERTION(mMainDecoder->IsDecoderAllowedToDownloadData(this),
|
||||
"Should not be called on non-active decoders!");
|
||||
|
||||
// Cannot have empty byte ranges.
|
||||
if (mByteRanges.IsEmpty()) {
|
||||
LOG1("Error getting list of subsegment byte ranges.");
|
||||
DecodeError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get byte range for subsegment.
|
||||
int32_t subsegmentIdx = mMainDecoder->GetSubsegmentIndex(this);
|
||||
NS_ASSERTION(0 <= subsegmentIdx,
|
||||
"Subsegment index should be >= 0 for active decoders");
|
||||
if (subsegmentIdx >= 0 && (uint32_t)subsegmentIdx < mByteRanges.Length()) {
|
||||
mCurrentByteRange = mByteRanges[subsegmentIdx];
|
||||
mSubsegmentIdx = subsegmentIdx;
|
||||
} else {
|
||||
mCurrentByteRange.Clear();
|
||||
mSubsegmentIdx = -1;
|
||||
LOG("End of subsegments: index [%d] out of range.", subsegmentIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Request a seek for the first reader. Required so that the reader is
|
||||
// primed to start here, and will block subsequent subsegment seeks unless
|
||||
// the subsegment has been read.
|
||||
if (subsegmentIdx == 0) {
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mReader->RequestSeekToSubsegment(0);
|
||||
}
|
||||
|
||||
// Query resource for cached ranges; only download if it's not there.
|
||||
if (IsSubsegmentCached(mSubsegmentIdx)) {
|
||||
LOG("Subsegment [%d] bytes [%lld] to [%lld] already cached. No need to "
|
||||
"download.", mSubsegmentIdx,
|
||||
mCurrentByteRange.mStart, mCurrentByteRange.mEnd);
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &DASHRepDecoder::DoNotifyDownloadEnded);
|
||||
nsresult rv = NS_DispatchToMainThread(event);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error notifying subsegment [%d] cached: rv[0x%x].",
|
||||
mSubsegmentIdx, rv);
|
||||
NetworkError();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Open byte range corresponding to subsegment.
|
||||
nsresult rv = mResource->OpenByteRange(nullptr, mCurrentByteRange);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error opening byte range [%lld - %lld]: subsegmentIdx [%d] rv [%x].",
|
||||
mCurrentByteRange.mStart, mCurrentByteRange.mEnd, mSubsegmentIdx, rv);
|
||||
NetworkError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DASHRepDecoder::IsSubsegmentCached(int32_t aSubsegmentIdx)
|
||||
{
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
MediaByteRange byteRange = mByteRanges[aSubsegmentIdx];
|
||||
int64_t start = mResource->GetNextCachedData(byteRange.mStart);
|
||||
int64_t end = mResource->GetCachedDataEnd(byteRange.mStart);
|
||||
return (start == byteRange.mStart &&
|
||||
end >= byteRange.mEnd);
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::DoNotifyDownloadEnded()
|
||||
{
|
||||
NotifyDownloadEnded(NS_OK);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DASHRepDecoder::GetByteRangeForSeek(int64_t const aOffset,
|
||||
MediaByteRange& aByteRange)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
// Only check data ranges if they're available and if this decoder is active,
|
||||
// i.e. inactive rep decoders should only load metadata.
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
|
||||
for (uint32_t i = 0; i < mByteRanges.Length(); i++) {
|
||||
NS_ENSURE_FALSE(mByteRanges[i].IsNull(), NS_ERROR_NOT_INITIALIZED);
|
||||
// Check if |aOffset| lies within the current data range.
|
||||
if (mByteRanges[i].mStart <= aOffset && aOffset <= mByteRanges[i].mEnd) {
|
||||
if (mMainDecoder->IsDecoderAllowedToDownloadSubsegment(this, i)) {
|
||||
mCurrentByteRange = aByteRange = mByteRanges[i];
|
||||
mSubsegmentIdx = i;
|
||||
// XXX Hack: should be setting subsegment outside this function, but
|
||||
// need to review seeking for multiple switches anyhow.
|
||||
mMainDecoder->SetSubsegmentIndex(this, i);
|
||||
LOG("Getting DATA range [%d] for seek offset [%lld]: "
|
||||
"bytes [%lld] to [%lld]",
|
||||
i, aOffset, aByteRange.mStart, aByteRange.mEnd);
|
||||
return NS_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Don't allow metadata downloads once they're loaded and byte ranges have
|
||||
// been populated.
|
||||
bool canDownloadMetadata = mByteRanges.IsEmpty();
|
||||
if (canDownloadMetadata) {
|
||||
// Check metadata ranges; init range.
|
||||
if (mInitByteRange.mStart <= aOffset && aOffset <= mInitByteRange.mEnd) {
|
||||
mCurrentByteRange = aByteRange = mInitByteRange;
|
||||
mSubsegmentIdx = 0;
|
||||
LOG("Getting INIT range for seek offset [%lld]: bytes [%lld] to "
|
||||
"[%lld]", aOffset, aByteRange.mStart, aByteRange.mEnd);
|
||||
return NS_OK;
|
||||
}
|
||||
// ... index range.
|
||||
if (mIndexByteRange.mStart <= aOffset && aOffset <= mIndexByteRange.mEnd) {
|
||||
mCurrentByteRange = aByteRange = mIndexByteRange;
|
||||
mSubsegmentIdx = 0;
|
||||
LOG("Getting INDEXES range for seek offset [%lld]: bytes [%lld] to "
|
||||
"[%lld]", aOffset, aByteRange.mStart, aByteRange.mEnd);
|
||||
return NS_OK;
|
||||
}
|
||||
} else {
|
||||
LOG1("Metadata should be read; inhibiting further metadata downloads.");
|
||||
}
|
||||
|
||||
// If no byte range is found by this stage, clear the parameter and return.
|
||||
aByteRange.Clear();
|
||||
if (mByteRanges.IsEmpty() || !canDownloadMetadata) {
|
||||
// Assume mByteRanges will be populated after metadata is read.
|
||||
LOG("Data ranges not populated [%s]; metadata download restricted [%s]: "
|
||||
"offset[%lld].",
|
||||
(mByteRanges.IsEmpty() ? "yes" : "no"),
|
||||
(canDownloadMetadata ? "no" : "yes"), aOffset);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
} else {
|
||||
// Cannot seek to an unknown offset.
|
||||
// XXX Revisit this for dynamic MPD profiles if MPD is regularly updated.
|
||||
LOG("Error! Offset [%lld] is in an unknown range!", aOffset);
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::PrepareForSwitch()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
// Ensure that the media cache writes any data held in its partial block.
|
||||
mResource->FlushCache();
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NetworkError()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->NetworkError(); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetDuration(double aDuration)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->SetDuration(aDuration); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetInfinite(bool aInfinite)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->SetInfinite(aInfinite); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::SetMediaSeekable(bool aMediaSeekable)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
|
||||
"Should be on main thread or decode thread.");
|
||||
if (mMainDecoder) { mMainDecoder->SetMediaSeekable(aMediaSeekable); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::Progress(bool aTimer)
|
||||
{
|
||||
if (mMainDecoder) { mMainDecoder->Progress(aTimer); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
LOG("Data bytes [%lld - %lld] arrived via buffer [%p].",
|
||||
aOffset, aOffset+aLength, aBuffer);
|
||||
// Notify reader directly, since call to |MediaDecoderStateMachine|::
|
||||
// |NotifyDataArrived| will go to |DASHReader|::|NotifyDataArrived|, which
|
||||
// has no way to forward the notification to the correct sub-reader.
|
||||
if (mReader) {
|
||||
mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
}
|
||||
// Forward to main decoder which will notify state machine.
|
||||
if (mMainDecoder) {
|
||||
mMainDecoder->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NotifyBytesDownloaded()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->NotifyBytesDownloaded(); }
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::NotifySuspendedStatusChanged()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
if (mMainDecoder) { mMainDecoder->NotifySuspendedStatusChanged(); }
|
||||
}
|
||||
|
||||
bool
|
||||
DASHRepDecoder::OnStateMachineThread() const
|
||||
{
|
||||
return (mMainDecoder ? mMainDecoder->OnStateMachineThread() : false);
|
||||
}
|
||||
|
||||
bool
|
||||
DASHRepDecoder::OnDecodeThread() const
|
||||
{
|
||||
return (mMainDecoder ? mMainDecoder->OnDecodeThread() : false);
|
||||
}
|
||||
|
||||
ReentrantMonitor&
|
||||
DASHRepDecoder::GetReentrantMonitor()
|
||||
{
|
||||
NS_ASSERTION(mMainDecoder, "Can't get monitor if main decoder is null!");
|
||||
if (mMainDecoder) {
|
||||
return mMainDecoder->GetReentrantMonitor();
|
||||
} else {
|
||||
// XXX If mMainDecoder is gone, most likely we're past shutdown and
|
||||
// a waiting function has been wakened. Just return this decoder's own
|
||||
// monitor and let the function complete.
|
||||
return MediaDecoder::GetReentrantMonitor();
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::layers::ImageContainer*
|
||||
DASHRepDecoder::GetImageContainer()
|
||||
{
|
||||
return (mMainDecoder ? mMainDecoder->GetImageContainer() : nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::DecodeError()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
MediaDecoder::DecodeError();
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &MediaDecoder::DecodeError);
|
||||
nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("Error dispatching DecodeError event to main thread: rv[%x]", rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DASHRepDecoder::ReleaseStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
|
||||
|
||||
// Since state machine owns mReader, remove reference to it.
|
||||
mReader = nullptr;
|
||||
|
||||
MediaDecoder::ReleaseStateMachine();
|
||||
}
|
||||
|
||||
void DASHRepDecoder::StopProgressUpdates()
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(mMainDecoder);
|
||||
MediaDecoder::StopProgressUpdates();
|
||||
}
|
||||
|
||||
void DASHRepDecoder::StartProgressUpdates()
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(mMainDecoder);
|
||||
MediaDecoder::StartProgressUpdates();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -1,237 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
|
||||
/* This Source Code Form Is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* DASH - Dynamic Adaptive Streaming over HTTP
|
||||
*
|
||||
* DASH is an adaptive bitrate streaming technology where a multimedia file is
|
||||
* partitioned into one or more segments and delivered to a client using HTTP.
|
||||
*
|
||||
* see DASHDecoder.cpp for info on DASH interaction with the media engine.*/
|
||||
|
||||
#if !defined(DASHRepDecoder_h_)
|
||||
#define DASHRepDecoder_h_
|
||||
|
||||
#include "Representation.h"
|
||||
#include "DASHDecoder.h"
|
||||
#include "WebMDecoder.h"
|
||||
#include "WebMReader.h"
|
||||
#include "MediaDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class ImageContainer;
|
||||
}
|
||||
|
||||
class DASHDecoder;
|
||||
class DASHRepReader;
|
||||
|
||||
class DASHRepDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
typedef mozilla::net::Representation Representation;
|
||||
typedef mozilla::net::SegmentBase SegmentBase;
|
||||
typedef mozilla::layers::ImageContainer ImageContainer;
|
||||
|
||||
// Constructor takes a ptr to the main decoder.
|
||||
DASHRepDecoder(DASHDecoder* aMainDecoder) :
|
||||
mMainDecoder(aMainDecoder),
|
||||
mMPDRepresentation(nullptr),
|
||||
mMetadataChunkCount(0),
|
||||
mCurrentByteRange(),
|
||||
mSubsegmentIdx(-1),
|
||||
mReader(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DASHRepDecoder);
|
||||
}
|
||||
|
||||
~DASHRepDecoder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DASHRepDecoder);
|
||||
}
|
||||
|
||||
// Clone not supported; just return nullptr.
|
||||
virtual MediaDecoder* Clone() { return nullptr; }
|
||||
|
||||
// Called by the main decoder at creation time; points to the main state
|
||||
// machine managed by the main decoder. Called on the main thread only.
|
||||
nsresult SetStateMachine(MediaDecoderStateMachine* aSM);
|
||||
|
||||
private:
|
||||
// Overridden to return the ptr set by SetStateMachine. Called on the main
|
||||
// thread only.
|
||||
MediaDecoderStateMachine* CreateStateMachine();
|
||||
|
||||
public:
|
||||
// Called by DASHDecoder at creation time; points to the media resource
|
||||
// for this decoder's |Representation|. Called on the main thread only.
|
||||
void SetResource(MediaResource* aResource);
|
||||
|
||||
// Sets the |Representation| object for this decoder. Called on the main
|
||||
// thread.
|
||||
void SetMPDRepresentation(Representation const * aRep);
|
||||
|
||||
// Called from DASHDecoder on main thread; Starts media stream download.
|
||||
virtual nsresult Load(nsIStreamListener** aListener = nullptr,
|
||||
MediaDecoder* aCloneDonor = nullptr) MOZ_OVERRIDE;
|
||||
|
||||
// Loads the next byte range (or first one on first call). Called on the main
|
||||
// thread only.
|
||||
void LoadNextByteRange();
|
||||
|
||||
// Returns true if the subsegment is already in the media cache.
|
||||
bool IsSubsegmentCached(int32_t aSubsegmentIdx);
|
||||
|
||||
// Calls from DASHRepDecoder. Called on the main thread only.
|
||||
void SetReader(WebMReader* aReader);
|
||||
|
||||
// Called if the media file encounters a network error. Call on the main
|
||||
// thread only.
|
||||
void NetworkError();
|
||||
|
||||
// Called from reader during ReadMetadata. This should be ignored here, and
|
||||
// instead, duration should be set following MPD parsing.
|
||||
void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE { };
|
||||
|
||||
// Set the duration of the media resource in units of seconds.
|
||||
// This is called via a channel listener if it can pick up the duration
|
||||
// from a content header. Must be called from the main thread only.
|
||||
virtual void SetDuration(double aDuration);
|
||||
|
||||
// Set media stream as infinite. Called on the main thread only.
|
||||
void SetInfinite(bool aInfinite);
|
||||
|
||||
// Sets media stream as seekable. Called on main thread only.
|
||||
void SetMediaSeekable(bool aSeekable);
|
||||
|
||||
// Fire progress events if needed according to the time and byte
|
||||
// constraints outlined in the specification. aTimer is true
|
||||
// if the method is called as a result of the progress timer rather
|
||||
// than the result of downloaded data.
|
||||
void Progress(bool aTimer);
|
||||
|
||||
// Called as data arrives on the stream and is read into the cache. Called
|
||||
// on the main thread only.
|
||||
void NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset);
|
||||
|
||||
// Called by MediaResource when some data has been received.
|
||||
// Call on the main thread only.
|
||||
void NotifyBytesDownloaded();
|
||||
|
||||
// Notify that a byte range request has been completed by the media resource.
|
||||
// Called on the main thread only.
|
||||
void NotifyDownloadEnded(nsresult aStatus);
|
||||
|
||||
// Called asynchronously by |LoadNextByteRange| if the data is already in the
|
||||
// media cache. This will call NotifyDownloadEnded on the main thread with
|
||||
// |aStatus| of NS_OK.
|
||||
void DoNotifyDownloadEnded();
|
||||
|
||||
// Called by MediaResource when the "cache suspended" status changes.
|
||||
// If MediaResource::IsSuspendedByCache returns true, then the decoder
|
||||
// should stop buffering or otherwise waiting for download progress and
|
||||
// start consuming data, if possible, because the cache is full.
|
||||
void NotifySuspendedStatusChanged();
|
||||
|
||||
// Increments the parsed and decoded frame counters by the passed in counts.
|
||||
// Can be called on any thread.
|
||||
void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_OVERRIDE {
|
||||
if (mMainDecoder) {mMainDecoder->NotifyDecodedFrames(aParsed, aDecoded); }
|
||||
}
|
||||
|
||||
// Gets a byte range containing the byte offset. Call on main thread only.
|
||||
nsresult GetByteRangeForSeek(int64_t const aOffset,
|
||||
MediaByteRange& aByteRange);
|
||||
|
||||
// Gets the number of data byte ranges (not inc. metadata).
|
||||
uint32_t GetNumDataByteRanges() {
|
||||
return mByteRanges.Length();
|
||||
}
|
||||
|
||||
// Notify that a switch is about to happen. Called on the main thread.
|
||||
void PrepareForSwitch();
|
||||
|
||||
// Returns true if the current thread is the state machine thread.
|
||||
bool OnStateMachineThread() const MOZ_OVERRIDE;
|
||||
|
||||
// Returns true if the current thread is the decode thread.
|
||||
bool OnDecodeThread() const MOZ_OVERRIDE;
|
||||
|
||||
// Returns main decoder's monitor for synchronised access.
|
||||
ReentrantMonitor& GetReentrantMonitor() MOZ_OVERRIDE;
|
||||
|
||||
// Called on the decode thread from WebMReader.
|
||||
ImageContainer* GetImageContainer() MOZ_OVERRIDE;
|
||||
|
||||
// Called when Metadata has been read; notifies that index data is read.
|
||||
// Called on the decode thread only.
|
||||
void OnReadMetadataCompleted() MOZ_OVERRIDE;
|
||||
|
||||
// Stop updating the bytes downloaded for progress notifications. Called
|
||||
// when seeking to prevent wild changes to the progress notification.
|
||||
// Must be called with the decoder monitor held.
|
||||
void StopProgressUpdates() MOZ_OVERRIDE;
|
||||
|
||||
// Allow updating the bytes downloaded for progress notifications. Must
|
||||
// be called with the decoder monitor held.
|
||||
void StartProgressUpdates() MOZ_OVERRIDE;
|
||||
|
||||
// Overridden to cleanup ref to |DASHDecoder|. Called on main thread only.
|
||||
void Shutdown() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
// Remove ref to state machine before |MediaDecoder|::|Shutdown|, since
|
||||
// |DASHDecoder| is responsible for its shutdown.
|
||||
mDecoderStateMachine = nullptr;
|
||||
// Call parent class shutdown.
|
||||
MediaDecoder::Shutdown();
|
||||
NS_ENSURE_TRUE_VOID(mShuttingDown);
|
||||
// Cleanup ref to main decoder.
|
||||
mMainDecoder = nullptr;
|
||||
}
|
||||
|
||||
// Drop reference to state machine and mReader (owned by state machine).
|
||||
// Only called during shutdown dance.
|
||||
void ReleaseStateMachine();
|
||||
|
||||
// Notifies the element that decoding has failed.
|
||||
void DecodeError();
|
||||
|
||||
private:
|
||||
// Populates |mByteRanges| by calling |GetIndexByteRanges| from |mReader|.
|
||||
// Called on the main thread only.
|
||||
nsresult PopulateByteRanges();
|
||||
|
||||
// The main decoder.
|
||||
nsRefPtr<DASHDecoder> mMainDecoder;
|
||||
// This decoder's MPD |Representation| object.
|
||||
Representation const * mMPDRepresentation;
|
||||
|
||||
// Countdown var for loading metadata byte ranges.
|
||||
uint16_t mMetadataChunkCount;
|
||||
|
||||
// All the byte ranges for this |Representation|.
|
||||
nsTArray<MediaByteRange> mByteRanges;
|
||||
|
||||
// Byte range for the init and index bytes.
|
||||
MediaByteRange mInitByteRange;
|
||||
MediaByteRange mIndexByteRange;
|
||||
|
||||
// The current byte range being requested.
|
||||
MediaByteRange mCurrentByteRange;
|
||||
// Index of the current byte range. Initialized to -1.
|
||||
int32_t mSubsegmentIdx;
|
||||
|
||||
// Ptr to the reader object for this |Representation|. Owned by state
|
||||
// machine.
|
||||
DASHRepReader* mReader;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif //DASHRepDecoder_h_
|
@ -1,68 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
|
||||
/* This Source Code Form Is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* DASH - Dynamic Adaptive Streaming over HTTP
|
||||
*
|
||||
* DASH is an adaptive bitrate streaming technology where a multimedia file is
|
||||
* partitioned into one or more segments and delivered to a client using HTTP.
|
||||
*
|
||||
* see DASHDecoder.cpp for comments on DASH object interaction
|
||||
*/
|
||||
|
||||
#if !defined(DASHRepReader_h_)
|
||||
#define DASHRepReader_h_
|
||||
|
||||
#include "VideoUtils.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "DASHReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DASHReader;
|
||||
|
||||
class DASHRepReader : public MediaDecoderReader
|
||||
{
|
||||
public:
|
||||
DASHRepReader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaDecoderReader(aDecoder) { }
|
||||
virtual ~DASHRepReader() { }
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DASHRepReader)
|
||||
|
||||
virtual void SetMainReader(DASHReader *aMainReader) = 0;
|
||||
|
||||
// Sets range for initialization bytes; used by DASH.
|
||||
virtual void SetInitByteRange(MediaByteRange &aByteRange) = 0;
|
||||
|
||||
// Sets range for index frame bytes; used by DASH.
|
||||
virtual void SetIndexByteRange(MediaByteRange &aByteRange) = 0;
|
||||
|
||||
// Returns the index of the subsegment which contains the seek time (usecs).
|
||||
virtual int64_t GetSubsegmentForSeekTime(int64_t aSeekToTime) = 0;
|
||||
|
||||
// Returns list of ranges for index frame start/end offsets. Used by DASH.
|
||||
virtual nsresult GetSubsegmentByteRanges(nsTArray<MediaByteRange>& aByteRanges) = 0;
|
||||
|
||||
// Returns true if the reader has reached a DASH switch access point.
|
||||
virtual bool HasReachedSubsegment(uint32_t aSubsegmentIndex) = 0;
|
||||
|
||||
// Requests a seek to the start of a particular DASH subsegment.
|
||||
virtual void RequestSeekToSubsegment(uint32_t aIdx) = 0;
|
||||
|
||||
// Reader should stop reading at the start of the specified subsegment, and
|
||||
// should prepare for the next reader to add data to the video queue.
|
||||
// Should be implemented by a sub-reader, e.g. |nsDASHWebMReader|.
|
||||
virtual void RequestSwitchAtSubsegment(int32_t aCluster,
|
||||
MediaDecoderReader* aNextReader) = 0;
|
||||
|
||||
// Returns true if data at the end of the final subsegment has been cached.
|
||||
virtual bool IsDataCachedAtEndOfSubsegments() = 0;
|
||||
};
|
||||
|
||||
}// namespace mozilla
|
||||
|
||||
#endif /*DASHRepReader*/
|
@ -1,19 +0,0 @@
|
||||
# -*- Mode: makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- #
|
||||
# vim: set ts=2 et sw=2 tw=80: #
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Steve Workman <sworkman@mozilla.com>
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES := \
|
||||
-I$(topsrcdir)/netwerk/dash/mpd \
|
||||
-I$(srcdir)/../webm \
|
||||
-I$(srcdir)/../../base/src \
|
||||
-I$(srcdir)/../../html/content/src \
|
||||
$(MOZ_LIBVPX_INCLUDES) \
|
||||
$(NULL)
|
@ -1,25 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
MODULE = 'content'
|
||||
|
||||
EXPORTS += [
|
||||
'DASHDecoder.h',
|
||||
'DASHReader.h',
|
||||
'DASHRepDecoder.h',
|
||||
'DASHRepReader.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'DASHDecoder.cpp',
|
||||
'DASHReader.cpp',
|
||||
'DASHRepDecoder.cpp',
|
||||
]
|
||||
|
||||
LIBRARY_NAME = 'gkcondash_s'
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
@ -26,9 +26,6 @@ if CONFIG['MOZ_WEBM']:
|
||||
if CONFIG['MOZ_GSTREAMER']:
|
||||
PARALLEL_DIRS += ['gstreamer']
|
||||
|
||||
if CONFIG['MOZ_DASH']:
|
||||
PARALLEL_DIRS += ['dash']
|
||||
|
||||
if CONFIG['MOZ_DIRECTSHOW']:
|
||||
PARALLEL_DIRS += ['directshow']
|
||||
|
||||
|
@ -381,26 +381,6 @@ MOCHITEST_FILES += \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_DASH
|
||||
MOCHITEST_FILES += \
|
||||
test_can_play_type_dash.html \
|
||||
dash/dash-manifest.mpd \
|
||||
dash/dash-manifest-sjs.mpd \
|
||||
test_dash_detect_stream_switch.html \
|
||||
dash_detect_stream_switch.sjs \
|
||||
dash/dash-webm-video-320x180.webm \
|
||||
dash/dash-webm-video-428x240.webm \
|
||||
dash/dash-webm-audio-128k.webm \
|
||||
dash/dash-manifest-garbled.mpd \
|
||||
dash/dash-manifest-garbled-webm.mpd \
|
||||
dash/garbled.webm \
|
||||
$(NULL)
|
||||
else
|
||||
MOCHITEST_FILES += \
|
||||
test_can_play_type_no_dash.html \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_WAVE
|
||||
MOCHITEST_FILES += \
|
||||
test_can_play_type_wave.html \
|
||||
|
@ -1,29 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=792935
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 792935: DASH: Add DASH-WebM cases to mochitests</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=792935">Mozilla Bug 792935: DASH: Add DASH-WebM cases to mochitests</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
<video id="v"></video>
|
||||
|
||||
<pre id="test">
|
||||
<script src="can_play_type_dash.js"></script>
|
||||
<script>
|
||||
var enabledByPref = SpecialPowers.getBoolPref("media.dash.enabled");
|
||||
check_dash(document.getElementById('v'), enabledByPref);
|
||||
mediaTestCleanup();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,28 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=792935
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 792935: DASH: Add DASH-WebM cases to mochitests</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=792935">Mozilla Bug 792935: DASH: Add DASH-WebM cases to mochitests</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
<video id="v"></video>
|
||||
|
||||
<pre id="test">
|
||||
<script src="can_play_type_dash.js"></script>
|
||||
<script>
|
||||
check_dash(document.getElementById('v'), false);
|
||||
mediaTestCleanup();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,81 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=792935
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 792935 - DASH Stream Switching</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=792935">Mozilla Bug 792935</a>
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript">
|
||||
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function createTestArray() {
|
||||
var tests = [];
|
||||
var tmpVid = document.createElement("video");
|
||||
|
||||
for (var testNum=0; testNum<gStreamSwitchTests.length; testNum++) {
|
||||
var test = gStreamSwitchTests[testNum];
|
||||
if (!tmpVid.canPlayType(test.type)) {
|
||||
continue;
|
||||
}
|
||||
tests.push(test);
|
||||
}
|
||||
return tests;
|
||||
}
|
||||
|
||||
function ended(evt) {
|
||||
var v = evt.target;
|
||||
if (v.parentNode) {
|
||||
v.parentNode.removeChild(v);
|
||||
}
|
||||
ok(true, "Fully played DASH video implies correct byte ranges downloaded.");
|
||||
dump("STREAM-SWITCH-TEST: Finished " + v._name + "\n");
|
||||
manager.finished(v.token);
|
||||
}
|
||||
|
||||
function error(evt) {
|
||||
var v = evt.target;
|
||||
ok(false, "Error suggests wrong byte range requested for " + v._name);
|
||||
}
|
||||
|
||||
function startTest(test, token) {
|
||||
var v = document.createElement('video');
|
||||
manager.started(token);
|
||||
v.type = test.type;
|
||||
v.src = test.name;
|
||||
v._name = test.name + " stream switch test";
|
||||
v.token = token;
|
||||
v.autoplay = true;
|
||||
|
||||
v.addEventListener("error", error, false);
|
||||
v.addEventListener("ended", ended, false);
|
||||
|
||||
dump("STREAM-SWITCH-TEST: Started " + name + "\n");
|
||||
document.body.appendChild(v);
|
||||
}
|
||||
|
||||
var testArray = createTestArray();
|
||||
if (testArray && testArray.length > 0) {
|
||||
manager.runTests(testArray, startTest);
|
||||
} else {
|
||||
var v = document.createElement('video');
|
||||
var canPlay = v.canPlayType("application/dash+xml");
|
||||
// If the test array is empty even though DASH is enabled, the test should
|
||||
// fail, i.e. canPlay should be false when testArray is empty.
|
||||
todo(canPlay, "No types supported - DASH " +
|
||||
(canPlay ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -8,8 +8,3 @@ LOCAL_INCLUDES = \
|
||||
$(MOZ_LIBVPX_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_DASH
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(srcdir)/../dash \
|
||||
$(NULL)
|
||||
endif
|
||||
|
@ -48,9 +48,6 @@ PRLogModuleInfo* gNesteggLog;
|
||||
|
||||
static const unsigned NS_PER_USEC = 1000;
|
||||
static const double NS_PER_S = 1e9;
|
||||
#ifdef MOZ_DASH
|
||||
static const double USEC_PER_S = 1e6;
|
||||
#endif
|
||||
|
||||
// Functions for reading and seeking using MediaResource required for
|
||||
// nestegg_io. The 'user data' passed to these functions is the
|
||||
@ -140,11 +137,7 @@ static void webm_log(nestegg * context,
|
||||
}
|
||||
|
||||
WebMReader::WebMReader(AbstractMediaDecoder* aDecoder)
|
||||
#ifdef MOZ_DASH
|
||||
: DASHRepReader(aDecoder),
|
||||
#else
|
||||
: MediaDecoderReader(aDecoder),
|
||||
#endif
|
||||
mContext(nullptr),
|
||||
mPacketCount(0),
|
||||
mChannels(0),
|
||||
@ -154,16 +147,6 @@ WebMReader::WebMReader(AbstractMediaDecoder* aDecoder)
|
||||
mAudioFrames(0),
|
||||
mHasVideo(false),
|
||||
mHasAudio(false)
|
||||
#ifdef MOZ_DASH
|
||||
, mMainReader(nullptr),
|
||||
mSwitchingCluster(-1),
|
||||
mNextReader(nullptr),
|
||||
mSeekToCluster(-1),
|
||||
mCurrentOffset(-1),
|
||||
mNextCluster(1),
|
||||
mPushVideoPacketToNextReader(false),
|
||||
mReachedSwitchAccessPoint(false)
|
||||
#endif
|
||||
{
|
||||
MOZ_COUNT_CTOR(WebMReader);
|
||||
#ifdef PR_LOGGING
|
||||
@ -234,16 +217,6 @@ nsresult WebMReader::ResetDecode()
|
||||
mVideoPackets.Reset();
|
||||
mAudioPackets.Reset();
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
LOG(PR_LOG_DEBUG, ("Resetting DASH seek vars"));
|
||||
mSwitchingCluster = -1;
|
||||
mNextReader = nullptr;
|
||||
mSeekToCluster = -1;
|
||||
mCurrentOffset = -1;
|
||||
mPushVideoPacketToNextReader = false;
|
||||
mReachedSwitchAccessPoint = false;
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -260,23 +233,12 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
LOG(PR_LOG_DEBUG, ("Reader [%p] for Decoder [%p]: Reading WebM Metadata: "
|
||||
"init bytes [%d - %d] cues bytes [%d - %d]",
|
||||
this, mDecoder,
|
||||
mInitByteRange.mStart, mInitByteRange.mEnd,
|
||||
mCuesByteRange.mStart, mCuesByteRange.mEnd));
|
||||
#endif
|
||||
nestegg_io io;
|
||||
io.read = webm_read;
|
||||
io.seek = webm_seek;
|
||||
io.tell = webm_tell;
|
||||
io.userdata = mDecoder;
|
||||
#ifdef MOZ_DASH
|
||||
int64_t maxOffset = mInitByteRange.IsNull() ? -1 : mInitByteRange.mEnd;
|
||||
#else
|
||||
int64_t maxOffset = -1;
|
||||
#endif
|
||||
int r = nestegg_init(&mContext, io, &webm_log, maxOffset);
|
||||
if (r == -1) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -424,45 +386,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
// Byte range for cues has been specified; load them.
|
||||
if (!mCuesByteRange.IsNull()) {
|
||||
maxOffset = mCuesByteRange.mEnd;
|
||||
|
||||
// Iterate through cluster ranges until nestegg returns the last one
|
||||
NS_ENSURE_TRUE(mClusterByteRanges.IsEmpty(),
|
||||
NS_ERROR_ALREADY_INITIALIZED);
|
||||
int clusterNum = 0;
|
||||
bool done = false;
|
||||
uint64_t timestamp;
|
||||
do {
|
||||
mClusterByteRanges.AppendElement();
|
||||
r = nestegg_get_cue_point(mContext, clusterNum, maxOffset,
|
||||
&(mClusterByteRanges[clusterNum].mStart),
|
||||
&(mClusterByteRanges[clusterNum].mEnd),
|
||||
×tamp);
|
||||
if (r != 0) {
|
||||
Cleanup();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOG(PR_LOG_DEBUG, ("Reader [%p] for Decoder [%p]: Cluster [%d]: "
|
||||
"start [%lld] end [%lld], timestamp [%.2llfs]",
|
||||
this, mDecoder, clusterNum,
|
||||
mClusterByteRanges[clusterNum].mStart,
|
||||
mClusterByteRanges[clusterNum].mEnd,
|
||||
timestamp/NS_PER_S));
|
||||
mClusterByteRanges[clusterNum].mStartTime = timestamp/NS_PER_USEC;
|
||||
// Last cluster will have '-1' as end value
|
||||
if (mClusterByteRanges[clusterNum].mEnd == -1) {
|
||||
mClusterByteRanges[clusterNum].mEnd = (mCuesByteRange.mStart-1);
|
||||
done = true;
|
||||
} else {
|
||||
clusterNum++;
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
#endif
|
||||
|
||||
// We can't seek in buffered regions if we have no cues.
|
||||
mDecoder->SetMediaSeekable(nestegg_has_cues(mContext) == 1);
|
||||
|
||||
@ -470,10 +393,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
||||
|
||||
*aTags = nullptr;
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
mDecoder->OnReadMetadataCompleted();
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -611,93 +530,6 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
||||
}
|
||||
|
||||
nsReturnRef<NesteggPacketHolder> WebMReader::NextPacket(TrackType aTrackType)
|
||||
#ifdef MOZ_DASH
|
||||
{
|
||||
nsAutoRef<NesteggPacketHolder> holder;
|
||||
// It is possible that following a seek, a requested switching offset could
|
||||
// be reached before |DASHReader| calls |RequestSwitchAtSubsegment|. In this
|
||||
// case |mNextReader| will be null, so check its value and at every possible
|
||||
// switch access point, i.e. cluster boundary, ask |mMainReader| to
|
||||
// |GetReaderForSubsegment|.
|
||||
if (mMainReader && !mNextReader && aTrackType == VIDEO) {
|
||||
WebMReader* nextReader = nullptr;
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("WebMReader[%p] for decoder [%p] NextPacket mNextReader not set: "
|
||||
"mCurrentOffset[%lld] nextCluster [%d] comparing with offset[%lld]",
|
||||
this, mDecoder, mCurrentOffset, mNextCluster,
|
||||
mClusterByteRanges[mNextCluster].mStart));
|
||||
|
||||
if (mNextCluster < mClusterByteRanges.Length() &&
|
||||
mCurrentOffset == mClusterByteRanges[mNextCluster].mStart) {
|
||||
DASHRepReader* nextDASHRepReader =
|
||||
mMainReader->GetReaderForSubsegment(mNextCluster);
|
||||
nextReader = static_cast<WebMReader*>(nextDASHRepReader);
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("WebMReader[%p] for decoder [%p] reached SAP at cluster [%d]: next "
|
||||
"reader is [%p]", this, mDecoder, mNextCluster, nextReader));
|
||||
if (nextReader && nextReader != this) {
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
// Ensure this reader is set to switch for the next packet.
|
||||
RequestSwitchAtSubsegment(mNextCluster, nextReader);
|
||||
NS_ASSERTION(mNextReader == nextReader, "mNextReader should be set");
|
||||
// Ensure the next reader seeks to |mNextCluster|. |PrepareToDecode|
|
||||
// must be called to ensure the reader's variables are set correctly.
|
||||
nextReader->RequestSeekToSubsegment(mNextCluster);
|
||||
nextReader->PrepareToDecode();
|
||||
}
|
||||
}
|
||||
// Keep mNextCluster up-to-date with the |mCurrentOffset|.
|
||||
if (mNextCluster+1 < mClusterByteRanges.Length()) {
|
||||
// At least one more cluster to go.
|
||||
mNextCluster++;
|
||||
} else {
|
||||
// Reached last cluster; prepare for being in cluster 0 again.
|
||||
mNextCluster = 1;
|
||||
}
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("WebMReader [%p] for decoder [%p] updating mNextCluster to [%d] "
|
||||
"at offset [%lld]", this, mDecoder, mNextCluster, mCurrentOffset));
|
||||
}
|
||||
}
|
||||
|
||||
// Get packet from next reader if we're at a switching point; most likely we
|
||||
// did not download the next packet for this reader's stream, so we have to
|
||||
// get it from the next one. Note: Switch to next reader only for video;
|
||||
// audio switching is not supported in the DASH-WebM On Demand profile.
|
||||
if (aTrackType == VIDEO &&
|
||||
(uint32_t)mSwitchingCluster < mClusterByteRanges.Length() &&
|
||||
mCurrentOffset == mClusterByteRanges[mSwitchingCluster].mStart) {
|
||||
|
||||
if (mVideoPackets.GetSize() > 0) {
|
||||
holder = NextPacketInternal(VIDEO);
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("WebMReader[%p] got packet from mVideoPackets @[%lld]",
|
||||
this, holder->mOffset));
|
||||
} else {
|
||||
mReachedSwitchAccessPoint = true;
|
||||
NS_ASSERTION(mNextReader,
|
||||
"Stream switch has been requested but mNextReader is null");
|
||||
holder = mNextReader->NextPacket(aTrackType);
|
||||
mPushVideoPacketToNextReader = true;
|
||||
// Reset for possible future switches.
|
||||
mSwitchingCluster = -1;
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("WebMReader[%p] got packet from mNextReader[%p] @[%lld]",
|
||||
this, mNextReader.get(), (holder ? holder->mOffset : 0)));
|
||||
}
|
||||
} else {
|
||||
holder = NextPacketInternal(aTrackType);
|
||||
if (holder) {
|
||||
mCurrentOffset = holder->mOffset;
|
||||
}
|
||||
}
|
||||
return holder.out();
|
||||
}
|
||||
|
||||
nsReturnRef<NesteggPacketHolder>
|
||||
WebMReader::NextPacketInternal(TrackType aTrackType)
|
||||
#endif
|
||||
{
|
||||
// The packet queue that packets will be pushed on if they
|
||||
// are not the type we are interested in.
|
||||
@ -928,18 +760,7 @@ bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
void
|
||||
WebMReader::PushVideoPacket(NesteggPacketHolder* aItem)
|
||||
{
|
||||
#ifdef MOZ_DASH
|
||||
if (mPushVideoPacketToNextReader) {
|
||||
NS_ASSERTION(mNextReader,
|
||||
"Stream switch has been requested but mNextReader is null");
|
||||
mNextReader->mVideoPackets.PushFront(aItem);
|
||||
mPushVideoPacketToNextReader = false;
|
||||
} else {
|
||||
#endif
|
||||
mVideoPackets.PushFront(aItem);
|
||||
#ifdef MOZ_DASH
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime,
|
||||
@ -957,56 +778,9 @@ nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime,
|
||||
if (r != 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#ifdef MOZ_DASH
|
||||
// Find next cluster index;
|
||||
MediaResource* resource = mDecoder->GetResource();
|
||||
int64_t newOffset = resource->Tell();
|
||||
for (uint32_t i = 1; i < mClusterByteRanges.Length(); i++) {
|
||||
if (newOffset < mClusterByteRanges[i].mStart) {
|
||||
mNextCluster = i;
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("WebMReader [%p] for decoder [%p] updating mNextCluster to [%d] "
|
||||
"after seek to offset [%lld]",
|
||||
this, mDecoder, mNextCluster, resource->Tell()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return DecodeToTarget(aTarget);
|
||||
}
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
bool WebMReader::IsDataCachedAtEndOfSubsegments()
|
||||
{
|
||||
MediaResource* resource = mDecoder->GetResource();
|
||||
NS_ENSURE_TRUE(resource, false);
|
||||
if (resource->IsDataCachedToEndOfResource(0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mClusterByteRanges.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<MediaByteRange> ranges;
|
||||
nsresult rv = resource->GetCachedRanges(ranges);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
if (ranges.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true if data at the end of the final subsegment is cached.
|
||||
uint32_t finalSubsegmentIndex = mClusterByteRanges.Length()-1;
|
||||
uint64_t finalSubEndOffset = mClusterByteRanges[finalSubsegmentIndex].mEnd;
|
||||
uint32_t finalRangeIndex = ranges.Length()-1;
|
||||
uint64_t finalRangeStartOffset = ranges[finalRangeIndex].mStart;
|
||||
uint64_t finalRangeEndOffset = ranges[finalRangeIndex].mEnd;
|
||||
|
||||
return (finalRangeStartOffset < finalSubEndOffset &&
|
||||
finalSubEndOffset <= finalRangeEndOffset);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
|
||||
{
|
||||
MediaResource* resource = mDecoder->GetResource();
|
||||
@ -1044,29 +818,6 @@ nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
|
||||
if (rv) {
|
||||
double startTime = start * timecodeScale / NS_PER_S - aStartTime;
|
||||
double endTime = end * timecodeScale / NS_PER_S - aStartTime;
|
||||
#ifdef MOZ_DASH
|
||||
// If this range extends to the end of a cluster, the true end time is
|
||||
// the cluster's end timestamp. Since WebM frames do not have an end
|
||||
// timestamp, a fully cached cluster must be reported with the correct
|
||||
// end time of its final frame. Otherwise, buffered ranges could be
|
||||
// reported with missing frames at cluster boundaries, specifically
|
||||
// boundaries where stream switching has occurred.
|
||||
if (!mClusterByteRanges.IsEmpty()) {
|
||||
for (uint32_t clusterIndex = 0;
|
||||
clusterIndex < (mClusterByteRanges.Length()-1);
|
||||
clusterIndex++) {
|
||||
if (ranges[index].mEnd >= mClusterByteRanges[clusterIndex].mEnd) {
|
||||
double clusterEndTime =
|
||||
mClusterByteRanges[clusterIndex+1].mStartTime / USEC_PER_S;
|
||||
if (endTime < clusterEndTime) {
|
||||
LOG(PR_LOG_DEBUG, ("End of cluster: endTime becoming %0.3fs",
|
||||
clusterEndTime));
|
||||
endTime = clusterEndTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// If this range extends to the end of the file, the true end time
|
||||
// is the file's duration.
|
||||
if (resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
|
||||
@ -1089,136 +840,4 @@ void WebMReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_
|
||||
mBufferedState->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
}
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
int64_t
|
||||
WebMReader::GetSubsegmentForSeekTime(int64_t aSeekToTime)
|
||||
{
|
||||
NS_ENSURE_TRUE(0 <= aSeekToTime, -1);
|
||||
// Check the first n-1 subsegments. End time is the start time of the next
|
||||
// subsegment.
|
||||
for (uint32_t i = 1; i < (mClusterByteRanges.Length()); i++) {
|
||||
if (aSeekToTime < mClusterByteRanges[i].mStartTime) {
|
||||
return i-1;
|
||||
}
|
||||
}
|
||||
// Check the last subsegment. End time is the end time of the file.
|
||||
NS_ASSERTION(mDecoder, "Decoder should not be null!");
|
||||
if (aSeekToTime <= mDecoder->GetMediaDuration()) {
|
||||
return mClusterByteRanges.Length()-1;
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
nsresult
|
||||
WebMReader::GetSubsegmentByteRanges(nsTArray<MediaByteRange>& aByteRanges)
|
||||
{
|
||||
NS_ENSURE_TRUE(mContext, NS_ERROR_NULL_POINTER);
|
||||
NS_ENSURE_TRUE(aByteRanges.IsEmpty(), NS_ERROR_ALREADY_INITIALIZED);
|
||||
NS_ENSURE_FALSE(mClusterByteRanges.IsEmpty(), NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_FALSE(mCuesByteRange.IsNull(), NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
for (uint32_t i = 0; i < mClusterByteRanges.Length(); i++) {
|
||||
aByteRanges.AppendElement();
|
||||
aByteRanges[i] = mClusterByteRanges[i];
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
WebMReader::RequestSwitchAtSubsegment(int32_t aSubsegmentIdx,
|
||||
MediaDecoderReader* aNextReader)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread() || mDecoder->OnDecodeThread(),
|
||||
"Should be on main thread or decode thread.");
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
// Only allow one switch at a time; ignore if one is already requested.
|
||||
if (mSwitchingCluster != -1) {
|
||||
return;
|
||||
}
|
||||
NS_ENSURE_TRUE_VOID((uint32_t)aSubsegmentIdx < mClusterByteRanges.Length());
|
||||
mSwitchingCluster = aSubsegmentIdx;
|
||||
NS_ENSURE_TRUE_VOID(aNextReader);
|
||||
NS_ENSURE_TRUE_VOID(aNextReader != this);
|
||||
mNextReader = static_cast<WebMReader*>(aNextReader);
|
||||
}
|
||||
|
||||
void
|
||||
WebMReader::RequestSeekToSubsegment(uint32_t aIdx)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread() || mDecoder->OnDecodeThread(),
|
||||
"Should be on main thread or decode thread.");
|
||||
NS_ASSERTION(mDecoder, "decoder should not be null!");
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
|
||||
// Don't seek if we're about to switch to another reader.
|
||||
if (mSwitchingCluster != -1) {
|
||||
return;
|
||||
}
|
||||
// Only allow seeking if a request was not already made.
|
||||
if (mSeekToCluster != -1) {
|
||||
return;
|
||||
}
|
||||
NS_ENSURE_TRUE_VOID(aIdx < mClusterByteRanges.Length());
|
||||
mSeekToCluster = aIdx;
|
||||
|
||||
// XXX Hack to get the resource to seek to the correct offset if the decode
|
||||
// thread is in shutdown, e.g. if the video is not autoplay.
|
||||
if (mDecoder->IsShutdown()) {
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
mDecoder->GetResource()->Seek(PR_SEEK_SET,
|
||||
mClusterByteRanges[mSeekToCluster].mStart);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebMReader::PrepareToDecode()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
if (mSeekToCluster != -1) {
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
SeekToCluster(mSeekToCluster);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebMReader::SeekToCluster(uint32_t aIdx)
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
NS_ENSURE_TRUE_VOID(aIdx < mClusterByteRanges.Length());
|
||||
LOG(PR_LOG_DEBUG, ("Reader [%p] for Decoder [%p]: seeking to "
|
||||
"subsegment [%lld] at offset [%lld]",
|
||||
this, mDecoder, aIdx, mClusterByteRanges[aIdx].mStart));
|
||||
int r = nestegg_offset_seek(mContext, mClusterByteRanges[aIdx].mStart);
|
||||
NS_ENSURE_TRUE_VOID(r == 0);
|
||||
if (aIdx + 1 < mClusterByteRanges.Length()) {
|
||||
mNextCluster = aIdx + 1;
|
||||
} else {
|
||||
mNextCluster = 1;
|
||||
}
|
||||
mSeekToCluster = -1;
|
||||
}
|
||||
|
||||
bool
|
||||
WebMReader::HasReachedSubsegment(uint32_t aSubsegmentIndex)
|
||||
{
|
||||
NS_ASSERTION(mDecoder, "Decoder is null.");
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
NS_ENSURE_TRUE(aSubsegmentIndex < mClusterByteRanges.Length(), false);
|
||||
|
||||
NS_ASSERTION(mDecoder->GetResource(), "Decoder has no media resource.");
|
||||
if (mReachedSwitchAccessPoint) {
|
||||
LOG(PR_LOG_DEBUG,
|
||||
("Reader [%p] for Decoder [%p]: reached switching offset [%lld] = "
|
||||
"mClusterByteRanges[%d].mStart[%lld]",
|
||||
this, mDecoder, mCurrentOffset, aSubsegmentIndex,
|
||||
mClusterByteRanges[aSubsegmentIndex].mStart));
|
||||
mReachedSwitchAccessPoint = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif /* MOZ_DASH */
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -22,10 +22,6 @@
|
||||
#include "vorbis/codec.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
#include "DASHRepReader.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WebMBufferedState;
|
||||
@ -101,11 +97,7 @@ class WebMPacketQueue : private nsDeque {
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
class WebMReader : public DASHRepReader
|
||||
#else
|
||||
class WebMReader : public MediaDecoderReader
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
WebMReader(AbstractMediaDecoder* aDecoder);
|
||||
@ -139,80 +131,6 @@ public:
|
||||
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
virtual void SetMainReader(DASHReader *aMainReader) MOZ_OVERRIDE {
|
||||
NS_ASSERTION(aMainReader, "aMainReader is null.");
|
||||
mMainReader = aMainReader;
|
||||
}
|
||||
|
||||
// Called by |DASHReader| on the decode thread so that this reader will
|
||||
// start reading at the appropriate subsegment/cluster. If this is not the
|
||||
// current reader and a switch was previously requested, then it will seek to
|
||||
// starting offset of the subsegment at which it is supposed to switch.
|
||||
// Called on the decode thread, enters the decode monitor.
|
||||
void PrepareToDecode() MOZ_OVERRIDE;
|
||||
|
||||
// Returns a reference to the audio/video queue of the main reader.
|
||||
// Allows for a single audio/video queue to be shared among multiple
|
||||
// |WebMReader|s.
|
||||
MediaQueue<AudioData>& AudioQueue() MOZ_OVERRIDE {
|
||||
if (mMainReader) {
|
||||
return mMainReader->AudioQueue();
|
||||
} else {
|
||||
return MediaDecoderReader::AudioQueue();
|
||||
}
|
||||
}
|
||||
|
||||
MediaQueue<VideoData>& VideoQueue() MOZ_OVERRIDE {
|
||||
if (mMainReader) {
|
||||
return mMainReader->VideoQueue();
|
||||
} else {
|
||||
return MediaDecoderReader::VideoQueue();
|
||||
}
|
||||
}
|
||||
|
||||
// Sets byte range for initialization (EBML); used by DASH.
|
||||
void SetInitByteRange(MediaByteRange &aByteRange) MOZ_OVERRIDE {
|
||||
mInitByteRange = aByteRange;
|
||||
}
|
||||
|
||||
// Sets byte range for cue points, i.e. cluster offsets; used by DASH.
|
||||
void SetIndexByteRange(MediaByteRange &aByteRange) MOZ_OVERRIDE {
|
||||
mCuesByteRange = aByteRange;
|
||||
}
|
||||
|
||||
// Returns the index of the subsegment which contains the seek time.
|
||||
int64_t GetSubsegmentForSeekTime(int64_t aSeekToTime) MOZ_OVERRIDE;
|
||||
|
||||
// Returns list of ranges for cluster start and end offsets.
|
||||
nsresult GetSubsegmentByteRanges(nsTArray<MediaByteRange>& aByteRanges)
|
||||
MOZ_OVERRIDE;
|
||||
|
||||
// Called by |DASHReader|::|PossiblySwitchVideoReaders| to check if this
|
||||
// reader has reached a switch access point and it's ok to switch readers.
|
||||
// Called on the decode thread.
|
||||
bool HasReachedSubsegment(uint32_t aSubsegmentIndex) MOZ_OVERRIDE;
|
||||
|
||||
// Requests that this reader seek to the specified subsegment. Seek will
|
||||
// happen when |PrepareDecodeVideoFrame| is called on the decode
|
||||
// thread.
|
||||
// Called on the main thread or decoder thread. Decode monitor must be held.
|
||||
void RequestSeekToSubsegment(uint32_t aIdx) MOZ_OVERRIDE;
|
||||
|
||||
// Requests that this reader switch to |aNextReader| at the start of the
|
||||
// specified subsegment. This is the reader to switch FROM.
|
||||
// Called on the main thread or decoder thread. Decode monitor must be held.
|
||||
void RequestSwitchAtSubsegment(int32_t aSubsegmentIdx,
|
||||
MediaDecoderReader* aNextReader) MOZ_OVERRIDE;
|
||||
|
||||
// Seeks to the beginning of the specified cluster. Called on the decode
|
||||
// thread.
|
||||
void SeekToCluster(uint32_t aIdx);
|
||||
|
||||
// Returns true if data at the end of the final subsegment has been cached.
|
||||
bool IsDataCachedAtEndOfSubsegments() MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Value passed to NextPacket to determine if we are reading a video or an
|
||||
// audio packet.
|
||||
@ -224,14 +142,6 @@ protected:
|
||||
// Read a packet from the nestegg file. Returns nullptr if all packets for
|
||||
// the particular track have been read. Pass VIDEO or AUDIO to indicate the
|
||||
// type of the packet we want to read.
|
||||
#ifdef MOZ_DASH
|
||||
nsReturnRef<NesteggPacketHolder> NextPacketInternal(TrackType aTrackType);
|
||||
|
||||
// Read a packet from the nestegg file. Returns nullptr if all packets for
|
||||
// the particular track have been read. Pass VIDEO or AUDIO to indicate the
|
||||
// type of the packet we want to read. If the reader reaches a switch access
|
||||
// point, this function will get a packet from |mNextReader|.
|
||||
#endif
|
||||
nsReturnRef<NesteggPacketHolder> NextPacket(TrackType aTrackType);
|
||||
|
||||
// Pushes a packet to the front of the video packet queue.
|
||||
@ -301,54 +211,6 @@ private:
|
||||
// Booleans to indicate if we have audio and/or video data
|
||||
bool mHasVideo;
|
||||
bool mHasAudio;
|
||||
|
||||
#ifdef MOZ_DASH
|
||||
// Byte range for initialisation data; e.g. specified in DASH manifest.
|
||||
MediaByteRange mInitByteRange;
|
||||
|
||||
// Byte range for cues; e.g. specified in DASH manifest.
|
||||
MediaByteRange mCuesByteRange;
|
||||
|
||||
// Byte ranges for clusters; set internally, derived from cues.
|
||||
nsTArray<TimestampedMediaByteRange> mClusterByteRanges;
|
||||
|
||||
// Pointer to the main |DASHReader|. Set in the constructor.
|
||||
DASHReader* mMainReader;
|
||||
|
||||
// Index of the cluster to switch to. Monitor must be entered for write
|
||||
// access on all threads, read access off the decode thread.
|
||||
int32_t mSwitchingCluster;
|
||||
|
||||
// Pointer to the next reader. Used in |NextPacket| and |PushVideoPacket| at
|
||||
// switch access points. Monitor must be entered for write access on all
|
||||
// threads, read access off the decode thread.
|
||||
nsRefPtr<WebMReader> mNextReader;
|
||||
|
||||
// Index of the cluster to seek to for a DASH stream request. Monitor must be
|
||||
// entered for write access on all threads, read access off the decode
|
||||
// thread.
|
||||
int32_t mSeekToCluster;
|
||||
|
||||
// Current end offset of the last packet read in |NextPacket|. Used to check
|
||||
// if the reader reached the switch access point. Accessed on the decode
|
||||
// thread only.
|
||||
int64_t mCurrentOffset;
|
||||
|
||||
// Index of next cluster to be read. Used to determine the starting offset of
|
||||
// the next cluster. Accessed on the decode thread only.
|
||||
uint32_t mNextCluster;
|
||||
|
||||
// Set in |NextPacket| if we read a packet from the next reader. If true in
|
||||
// |PushVideoPacket|, we will push the packet onto the next reader's
|
||||
// video packet queue (not video data queue!). Accessed on decode thread
|
||||
// only.
|
||||
bool mPushVideoPacketToNextReader;
|
||||
|
||||
// Indicates if the reader has reached a switch access point. Set in
|
||||
// |NextPacket| and read in |HasReachedSubsegment|. Accessed on
|
||||
// decode thread only.
|
||||
bool mReachedSwitchAccessPoint;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -14,6 +14,3 @@ EXPORT_RESOURCE_CONTENT = \
|
||||
$(NULL)
|
||||
libs::
|
||||
$(INSTALL) $(EXPORT_RESOURCE_CONTENT) $(DIST)/bin/res/dtd
|
||||
|
||||
install::
|
||||
$(SYSINSTALL) $(IFLAGS1) $(EXPORT_RESOURCE_CONTENT) $(DESTDIR)$(mozappdir)/res/dtd
|
||||
|
@ -248,6 +248,7 @@ BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue)
|
||||
nsresult rv;
|
||||
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
NS_ENSURE_TRUE_VOID(sc);
|
||||
|
||||
AutoPushJSContext cx(sc->GetNativeContext());
|
||||
JS::Rooted<JSObject*> uuids(cx);
|
||||
@ -263,6 +264,7 @@ BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue)
|
||||
nsresult rv;
|
||||
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
NS_ENSURE_TRUE_VOID(sc);
|
||||
|
||||
AutoPushJSContext cx(sc->GetNativeContext());
|
||||
JS::Rooted<JSObject*> deviceAddresses(cx);
|
||||
|
@ -125,6 +125,7 @@ BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
|
||||
nsresult rv;
|
||||
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
NS_ENSURE_TRUE_VOID(sc);
|
||||
|
||||
AutoPushJSContext cx(sc->GetNativeContext());
|
||||
|
||||
@ -140,6 +141,7 @@ BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
|
||||
nsresult rv;
|
||||
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
NS_ENSURE_TRUE_VOID(sc);
|
||||
|
||||
AutoPushJSContext cx(sc->GetNativeContext());
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "base/basictypes.h"
|
||||
#include "BluetoothOppManager.h"
|
||||
|
||||
#include "BluetoothProfileController.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "BluetoothSocket.h"
|
||||
#include "BluetoothUtils.h"
|
||||
@ -189,7 +188,6 @@ BluetoothOppManager::BluetoothOppManager() : mConnected(false)
|
||||
, mSentFileLength(0)
|
||||
, mWaitingToSendPutFinal(false)
|
||||
, mCurrentBlobIndex(-1)
|
||||
, mController(nullptr)
|
||||
{
|
||||
mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
|
||||
}
|
||||
@ -278,48 +276,6 @@ BluetoothOppManager::ConnectInternal(const nsAString& aDeviceAddress)
|
||||
new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::Connect(const nsAString& aDeviceAddress,
|
||||
BluetoothProfileController* aController)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aController && !mController);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs || sInShutdown) {
|
||||
aController->OnConnect(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSocket) {
|
||||
if (mConnectedDeviceAddress == aDeviceAddress) {
|
||||
aController->OnConnect(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
|
||||
} else {
|
||||
aController->OnConnect(NS_LITERAL_STRING(ERR_REACHED_CONNECTION_LIMIT));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mController = aController;
|
||||
OnConnect(EmptyString());
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
|
||||
{
|
||||
if (!mSocket) {
|
||||
if (aController) {
|
||||
aController->OnDisconnect(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mController);
|
||||
|
||||
mController = aController;
|
||||
mSocket->Disconnect();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::HandleShutdown()
|
||||
{
|
||||
@ -1582,37 +1538,27 @@ BluetoothOppManager::AcquireSdcardMountLock()
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::Connect(const nsAString& aDeviceAddress,
|
||||
BluetoothProfileController* aController)
|
||||
{
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
|
||||
{
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::OnConnect(const nsAString& aErrorStr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!aErrorStr.IsEmpty()) {
|
||||
mSocket = nullptr;
|
||||
Listen();
|
||||
}
|
||||
|
||||
/**
|
||||
* On the one hand, notify the controller that we've done for outbound
|
||||
* connections. On the other hand, we do nothing for inbound connections.
|
||||
*/
|
||||
NS_ENSURE_TRUE_VOID(mController);
|
||||
|
||||
nsRefPtr<BluetoothProfileController> controller = mController.forget();
|
||||
controller->OnConnect(aErrorStr);
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
/**
|
||||
* On the one hand, notify the controller that we've done for outbound
|
||||
* connections. On the other hand, we do nothing for inbound connections.
|
||||
*/
|
||||
NS_ENSURE_TRUE_VOID(mController);
|
||||
|
||||
nsRefPtr<BluetoothProfileController> controller = mController.forget();
|
||||
controller->OnDisconnect(aErrorStr);
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
@ -82,17 +82,6 @@ public:
|
||||
aName.AssignLiteral("OPP");
|
||||
}
|
||||
|
||||
/*
|
||||
* If an application wants to send a file, first, it needs to
|
||||
* call Connect() to create a valid RFCOMM connection. After
|
||||
* that, call SendFile()/StopSendingFile() to control file-sharing
|
||||
* process. During the file transfering process, the application
|
||||
* will receive several system messages which contain the processed
|
||||
* percentage of file. At the end, the application will get another
|
||||
* system message indicating that the process is complete, then it can
|
||||
* either call Disconnect() to close RFCOMM connection or start another
|
||||
* file-sending thread via calling SendFile() again.
|
||||
*/
|
||||
virtual void Connect(const nsAString& aDeviceAddress,
|
||||
BluetoothProfileController* aController) MOZ_OVERRIDE;
|
||||
virtual void Disconnect(BluetoothProfileController* aController) MOZ_OVERRIDE;
|
||||
@ -222,7 +211,6 @@ private:
|
||||
nsCOMPtr<nsIOutputStream> mOutputStream;
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
nsCOMPtr<nsIVolumeMountLock> mMountLock;
|
||||
nsRefPtr<BluetoothProfileController> mController;
|
||||
nsRefPtr<DeviceStorageFile> mDsFile;
|
||||
|
||||
// If a connection has been established, mSocket will be the socket
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "BluetoothA2dpManager.h"
|
||||
#include "BluetoothHfpManager.h"
|
||||
#include "BluetoothHidManager.h"
|
||||
#include "BluetoothOppManager.h"
|
||||
|
||||
#include "BluetoothUtils.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
@ -80,9 +79,6 @@ BluetoothProfileController::AddProfileWithServiceClass(
|
||||
case BluetoothServiceClass::A2DP:
|
||||
profile = BluetoothA2dpManager::Get();
|
||||
break;
|
||||
case BluetoothServiceClass::OBJECT_PUSH:
|
||||
profile = BluetoothOppManager::Get();
|
||||
break;
|
||||
case BluetoothServiceClass::HID:
|
||||
profile = BluetoothHidManager::Get();
|
||||
break;
|
||||
@ -132,7 +128,6 @@ BluetoothProfileController::SetupProfiles(bool aAssignServiceClass)
|
||||
// For a disconnect request, all connected profiles are put into array.
|
||||
if (!mConnect) {
|
||||
AddProfile(BluetoothHidManager::Get(), true);
|
||||
AddProfile(BluetoothOppManager::Get(), true);
|
||||
AddProfile(BluetoothA2dpManager::Get(), true);
|
||||
AddProfile(BluetoothHfpManager::Get(), true);
|
||||
return;
|
||||
@ -143,17 +138,13 @@ BluetoothProfileController::SetupProfiles(bool aAssignServiceClass)
|
||||
* all of them sequencely.
|
||||
*/
|
||||
bool hasAudio = HAS_AUDIO(mTarget.cod);
|
||||
bool hasObjectTransfer = HAS_OBJECT_TRANSFER(mTarget.cod);
|
||||
bool hasRendering = HAS_RENDERING(mTarget.cod);
|
||||
bool isPeripheral = IS_PERIPHERAL(mTarget.cod);
|
||||
|
||||
NS_ENSURE_TRUE_VOID(hasAudio || hasObjectTransfer ||
|
||||
hasRendering || isPeripheral);
|
||||
NS_ENSURE_TRUE_VOID(hasAudio || hasRendering || isPeripheral);
|
||||
|
||||
/**
|
||||
* Connect to HFP/HSP first. Then, connect A2DP if Rendering bit is set.
|
||||
* It's almost impossible to send file to a remote device which is an Audio
|
||||
* device or a Rendering device, so we won't connect OPP in that case.
|
||||
*/
|
||||
if (hasAudio) {
|
||||
AddProfile(BluetoothHfpManager::Get());
|
||||
@ -161,9 +152,6 @@ BluetoothProfileController::SetupProfiles(bool aAssignServiceClass)
|
||||
if (hasRendering) {
|
||||
AddProfile(BluetoothA2dpManager::Get());
|
||||
}
|
||||
if (hasObjectTransfer && !hasAudio && !hasRendering) {
|
||||
AddProfile(BluetoothOppManager::Get());
|
||||
}
|
||||
if (isPeripheral) {
|
||||
AddProfile(BluetoothHidManager::Get());
|
||||
}
|
||||
|
@ -37,9 +37,6 @@ BEGIN_BLUETOOTH_NAMESPACE
|
||||
// Bit 21: Major service class = 0x100, Audio
|
||||
#define HAS_AUDIO(cod) (cod & 0x200000)
|
||||
|
||||
// Bit 20: Major service class = 0x80, Object Transfer
|
||||
#define HAS_OBJECT_TRANSFER(cod) (cod & 0x100000)
|
||||
|
||||
// Bit 18: Major service class = 0x20, Rendering
|
||||
#define HAS_RENDERING(cod) (cod & 0x40000)
|
||||
|
||||
|
@ -205,8 +205,10 @@ function expectPriorityWithBackgroundLRUSet(childID, expectedBackgroundLRU) {
|
||||
'process-priority-with-background-LRU-set',
|
||||
function(subject, topic, data) {
|
||||
|
||||
dump("browserElementTestHelpers got notify: topic "+ topic + ", data " + data +"\n");
|
||||
[id, priority, cpuPriority, backgroundLRU] = data.split(":");
|
||||
if (id != childID) {
|
||||
dump("id(" + id + ") != childID(" + childID + ")\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1009,19 +1009,21 @@ var steps = [
|
||||
next();
|
||||
},
|
||||
function() {
|
||||
ok(true, "Undefined strings should be treated as null");
|
||||
ok(true, "Undefined properties of fields should be treated correctly");
|
||||
var c = new mozContact({
|
||||
adr: [{value: undefined}],
|
||||
adr: [{streetAddress: undefined}],
|
||||
email: [{value: undefined}],
|
||||
url: [{value: undefined}],
|
||||
impp: [{value: undefined}],
|
||||
tel: [{value: undefined}],
|
||||
});
|
||||
is(c.adr[0].type, null, "adr is null");
|
||||
is(c.email[0].type, null, "email is null");
|
||||
is(c.url[0].type, null, "url is null");
|
||||
is(c.impp[0].type, null, "impp is null");
|
||||
is(c.tel[0].type, null, "tel is null");
|
||||
ise(c.adr[0].streetAddress, null, "adr.streetAddress is null");
|
||||
ise(c.adr[0].locality, null, "adr.locality is null");
|
||||
ise(c.adr[0].pref, null, "adr.pref is null");
|
||||
ise(c.email[0].value, null, "email.value is null");
|
||||
ise(c.url[0].value, null, "url.value is null");
|
||||
ise(c.impp[0].value, null, "impp.value is null");
|
||||
ise(c.tel[0].value, null, "tel.value is null");
|
||||
next();
|
||||
},
|
||||
function() {
|
||||
|
@ -310,7 +310,7 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
|
||||
* 'pukRequired', 'personalizationInProgress', 'networkLocked',
|
||||
* 'corporateLocked', 'serviceProviderLocked', 'networkPukRequired',
|
||||
* 'corporatePukRequired', 'serviceProviderPukRequired',
|
||||
* 'personalizationReady', 'ready'.
|
||||
* 'personalizationReady', 'ready', 'permanentBlocked'.
|
||||
*/
|
||||
readonly attribute DOMString cardState;
|
||||
|
||||
|
48
dom/icc/tests/marionette/stk_helper.js
Normal file
48
dom/icc/tests/marionette/stk_helper.js
Normal file
@ -0,0 +1,48 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
if (expect) {
|
||||
func(evt.command, expect);
|
||||
} else {
|
||||
func(evt.command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect);
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testDisplayText(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -96,40 +91,4 @@ let tests = [
|
||||
timeInterval: 0x0A}}},
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testGetInKey(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -107,40 +102,4 @@ let tests = [
|
||||
timeInterval: 0x0A}}},
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testGetInput(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -174,40 +169,4 @@ let tests = [
|
||||
maxLength: 10}},
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testLaunchBrowser(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -251,40 +246,4 @@ let tests = [
|
||||
text: "ル"}}
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testPollOff(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -23,40 +18,4 @@ let tests = [
|
||||
commandQualifier: 0x00}}
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testLocalInfoLocation(cmd) {
|
||||
log("STK CMD " + JSON.stringify(cmd));
|
||||
@ -109,39 +104,4 @@ let tests = [
|
||||
func: testTimerManagementGetCurrentValue},
|
||||
];
|
||||
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(cmd, func) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(cmd, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let cmd = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(cmd, test.func)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testRefresh(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -27,40 +22,4 @@ let tests = [
|
||||
commandQualifier: 0x04}}
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testSelectItem(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -312,40 +307,4 @@ let tests = [
|
||||
items: [{identifier: 1, text: "82ル1"}, {identifier: 2, text: "82ル2"}, {identifier: 3, text: "82ル3"}]}}
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testSendDTMF(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -192,40 +187,4 @@ let tests = [
|
||||
text: "ル"}}
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testSendSMS(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -242,40 +237,4 @@ let tests = [
|
||||
title: "82ル2"}}
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testSendSS(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -202,40 +197,4 @@ let tests = [
|
||||
title: "ル"}}
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testSendUSSD(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -207,40 +202,4 @@ let tests = [
|
||||
title: "ル"}}
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testSetupCall(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -346,40 +341,4 @@ let tests = [
|
||||
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testSetupEventList(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -52,40 +47,4 @@ let tests = [
|
||||
eventList: [7]}}
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testSetupIdleModeText(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -195,40 +190,4 @@ let tests = [
|
||||
text: "80ル0"}},
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -1,12 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 30000;
|
||||
|
||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
||||
|
||||
let icc = navigator.mozIccManager;
|
||||
ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor);
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testSetupMenu(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
@ -234,40 +229,4 @@ let tests = [
|
||||
|
||||
];
|
||||
|
||||
// TODO - Bug 843455: Import scripts for marionette tests.
|
||||
let pendingEmulatorCmdCount = 0;
|
||||
function sendStkPduToEmulator(command, func, expect) {
|
||||
++pendingEmulatorCmdCount;
|
||||
|
||||
runEmulatorCmd(command, function (result) {
|
||||
--pendingEmulatorCmdCount;
|
||||
is(result[0], "OK");
|
||||
});
|
||||
|
||||
icc.onstkcommand = function (evt) {
|
||||
func(evt.command, expect);
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let test = tests.pop();
|
||||
if (!test) {
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let command = "stk pdu " + test.command;
|
||||
sendStkPduToEmulator(command, test.func, test.expect)
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
if (pendingEmulatorCmdCount) {
|
||||
window.setTimeout(cleanUp, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
SpecialPowers.removePermission("mobileconnection", document);
|
||||
finish();
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
|
@ -43,7 +43,9 @@
|
||||
//
|
||||
// (Wow, our logging story is a huge mess.)
|
||||
|
||||
// #define ENABLE_LOGGING 1
|
||||
#ifndef HAVE_64BIT_OS
|
||||
#define ENABLE_LOGGING 1
|
||||
#endif
|
||||
|
||||
#if defined(ANDROID) && defined(ENABLE_LOGGING)
|
||||
# include <android/log.h>
|
||||
|
@ -7,7 +7,7 @@
|
||||
if CONFIG['MOZ_WEBRTC']:
|
||||
DIRS += ['bridge']
|
||||
|
||||
TEST_DIRS += ['tests/mochitest']
|
||||
TEST_DIRS += ['tests/mochitest', 'tests/ipc']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDOMMediaStream.idl',
|
||||
|
4
dom/media/tests/ipc/Makefile.in
Normal file
4
dom/media/tests/ipc/Makefile.in
Normal file
@ -0,0 +1,4 @@
|
||||
# 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/.
|
||||
|
3
dom/media/tests/ipc/mochitest.ini
Normal file
3
dom/media/tests/ipc/mochitest.ini
Normal file
@ -0,0 +1,3 @@
|
||||
[DEFAULT]
|
||||
|
||||
[test_ipc.html]
|
@ -4,4 +4,4 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
PARALLEL_DIRS += ['mpd']
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
175
dom/media/tests/ipc/test_ipc.html
Normal file
175
dom/media/tests/ipc/test_ipc.html
Normal file
@ -0,0 +1,175 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for OOP PeerConncection</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// This isn't a single test. It runs the entirety of the PeerConnection
|
||||
// tests. Each of those has a normal timeout handler, so there's no point in
|
||||
// having a timeout here. I'm setting this really high just to avoid getting
|
||||
// killed.
|
||||
SimpleTest.requestLongerTimeout(100);
|
||||
|
||||
// Disable crash observers as it breaks later tests.
|
||||
function iframeScriptFirst() {
|
||||
SpecialPowers.prototype.registerProcessCrashObservers = function() { };
|
||||
SpecialPowers.prototype.unregisterProcessCrashObservers = function() { };
|
||||
|
||||
content.wrappedJSObject.RunSet.reloadAndRunAll({
|
||||
preventDefault: function() { },
|
||||
__exposedProps__: { preventDefault: 'r' }
|
||||
});
|
||||
}
|
||||
|
||||
function iframeScriptSecond() {
|
||||
let TestRunner = content.wrappedJSObject.TestRunner;
|
||||
|
||||
let oldComplete = TestRunner.onComplete;
|
||||
|
||||
TestRunner.onComplete = function() {
|
||||
TestRunner.onComplete = oldComplete;
|
||||
|
||||
sendAsyncMessage("test:PeerConnection:ipcTestComplete", {
|
||||
result: JSON.stringify(TestRunner._failedTests)
|
||||
});
|
||||
|
||||
if (oldComplete) {
|
||||
oldComplete();
|
||||
}
|
||||
};
|
||||
|
||||
let oldLog = TestRunner.log;
|
||||
TestRunner.log = function(msg) {
|
||||
sendAsyncMessage("test:PeerConnection:ipcTestMessage", { msg: msg });
|
||||
}
|
||||
}
|
||||
|
||||
let regex = /^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO) \| ([^\|]+) \|(.*)/;
|
||||
|
||||
function onTestMessage(data) {
|
||||
let message = SpecialPowers.wrap(data).json.msg;
|
||||
let match = regex.exec(message);
|
||||
if (match) {
|
||||
let state = match[1];
|
||||
let details = match[2] + " | " + match[3];
|
||||
|
||||
switch (state) {
|
||||
case "TEST-PASS":
|
||||
case "TEST-KNOWN-FAIL":
|
||||
ok(true, details);
|
||||
break;
|
||||
|
||||
case "TEST-UNEXPECTED-FAIL":
|
||||
case "TEST-UNEXPECTED-PASS":
|
||||
ok(false, details);
|
||||
break;
|
||||
|
||||
case "TEST-DEBUG-INFO":
|
||||
default:
|
||||
info(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onTestComplete() {
|
||||
let comp = SpecialPowers.wrap(SpecialPowers.Components);
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
let spObserver = comp.classes["@mozilla.org/special-powers-observer;1"]
|
||||
.getService(comp.interfaces.nsIMessageListener);
|
||||
|
||||
mm.removeMessageListener("SPPrefService", spObserver);
|
||||
mm.removeMessageListener("SPProcessCrashService", spObserver);
|
||||
mm.removeMessageListener("SPPingService", spObserver);
|
||||
mm.removeMessageListener("SpecialPowers.Quit", spObserver);
|
||||
mm.removeMessageListener("SPPermissionManager", spObserver);
|
||||
|
||||
mm.removeMessageListener("test:PeerConnection:ipcTestMessage", onTestMessage);
|
||||
mm.removeMessageListener("test:PeerConnection:ipcTestComplete", onTestComplete);
|
||||
|
||||
SimpleTest.executeSoon(function () { SimpleTest.finish(); });
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
let iframe = document.createElement("iframe");
|
||||
SpecialPowers.wrap(iframe).mozbrowser = true;
|
||||
iframe.id = "iframe";
|
||||
iframe.style.width = "100%";
|
||||
iframe.style.height = "1000px";
|
||||
|
||||
function iframeLoadSecond() {
|
||||
ok(true, "Got second iframe load event.");
|
||||
iframe.removeEventListener("mozbrowserloadend", iframeLoadSecond);
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
mm.loadFrameScript("data:,(" + iframeScriptSecond.toString() + ")();",
|
||||
false);
|
||||
}
|
||||
|
||||
function iframeLoadFirst() {
|
||||
ok(true, "Got first iframe load event.");
|
||||
iframe.removeEventListener("mozbrowserloadend", iframeLoadFirst);
|
||||
iframe.addEventListener("mozbrowserloadend", iframeLoadSecond);
|
||||
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
|
||||
let comp = SpecialPowers.wrap(SpecialPowers.Components);
|
||||
|
||||
let spObserver =
|
||||
comp.classes["@mozilla.org/special-powers-observer;1"]
|
||||
.getService(comp.interfaces.nsIMessageListener);
|
||||
|
||||
mm.addMessageListener("SPPrefService", spObserver);
|
||||
mm.addMessageListener("SPProcessCrashService", spObserver);
|
||||
mm.addMessageListener("SPPingService", spObserver);
|
||||
mm.addMessageListener("SpecialPowers.Quit", spObserver);
|
||||
mm.addMessageListener("SPPermissionManager", spObserver);
|
||||
|
||||
mm.addMessageListener("test:PeerConnection:ipcTestMessage", onTestMessage);
|
||||
mm.addMessageListener("test:PeerConnection:ipcTestComplete", onTestComplete);
|
||||
|
||||
let specialPowersBase = "chrome://specialpowers/content/";
|
||||
mm.loadFrameScript(specialPowersBase + "MozillaLogger.js", false);
|
||||
mm.loadFrameScript(specialPowersBase + "specialpowersAPI.js", false);
|
||||
mm.loadFrameScript(specialPowersBase + "specialpowers.js", false);
|
||||
|
||||
mm.loadFrameScript("data:,(" + iframeScriptFirst.toString() + ")();", false);
|
||||
}
|
||||
|
||||
iframe.addEventListener("mozbrowserloadend", iframeLoadFirst);
|
||||
|
||||
// Strip this filename and one directory level and then add "/mochitest".
|
||||
let href = window.location.href;
|
||||
href = href.substring(0, href.lastIndexOf('/'));
|
||||
href = href.substring(0, href.lastIndexOf('/'));
|
||||
iframe.src = href + "/mochitest?consoleLevel=INFO";
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener("load", function() {
|
||||
|
||||
SpecialPowers.addPermission("browser", true, document);
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["media.peerconnection.ipc.enabled", true],
|
||||
|
||||
// TODO: remove this as part of bug 820712
|
||||
["network.disable.ipc.security", true],
|
||||
|
||||
["dom.ipc.browser_frames.oop_by_default", true],
|
||||
["dom.mozBrowserFramesEnabled", true],
|
||||
["browser.pagethumbnails.capturing_disabled", true]
|
||||
]
|
||||
}, runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -13,6 +13,7 @@ XPIDL_SOURCES += [
|
||||
'nsITCPServerSocketParent.idl',
|
||||
'nsITCPSocketChild.idl',
|
||||
'nsITCPSocketParent.idl',
|
||||
'nsIUDPSocketChild.idl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
|
52
dom/network/interfaces/nsIUDPSocketChild.idl
Normal file
52
dom/network/interfaces/nsIUDPSocketChild.idl
Normal file
@ -0,0 +1,52 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsINetAddr.idl"
|
||||
|
||||
interface nsIUDPSocketInternal;
|
||||
|
||||
%{ C++
|
||||
#include "mozilla/net/DNS.h"
|
||||
%}
|
||||
native NetAddr(mozilla::net::NetAddr);
|
||||
[ptr] native NetAddrPtr(mozilla::net::NetAddr);
|
||||
|
||||
[scriptable, uuid(B47E5A0F-D384-48EF-8885-4259793D9CF0)]
|
||||
interface nsIUDPSocketChild : nsISupports
|
||||
{
|
||||
readonly attribute unsigned short localPort;
|
||||
readonly attribute AUTF8String localAddress;
|
||||
// Tell the chrome process to bind the UDP socket to a given local host and port
|
||||
void bind(in nsIUDPSocketInternal socket, in AUTF8String host, in unsigned short port);
|
||||
|
||||
// Tell the chrome process to perform equivalent operations to all following methods
|
||||
void send(in AUTF8String host, in unsigned short port,
|
||||
[const, array, size_is(byteLength)] in uint8_t bytes,
|
||||
in unsigned long byteLength);
|
||||
// Send without DNS query
|
||||
void sendWithAddr(in nsINetAddr addr,
|
||||
[const, array, size_is(byteLength)] in uint8_t bytes,
|
||||
in unsigned long byteLength);
|
||||
[noscript] void sendWithAddress([const] in NetAddrPtr addr,
|
||||
[const, array, size_is(byteLength)] in uint8_t bytes,
|
||||
in unsigned long byteLength);
|
||||
void close();
|
||||
};
|
||||
|
||||
/*
|
||||
* Internal interface for callback from chrome process
|
||||
*/
|
||||
[scriptable, uuid(1E27E9B3-C1C8-4B05-A415-1A2C1A641C60)]
|
||||
interface nsIUDPSocketInternal : nsISupports
|
||||
{
|
||||
void callListenerError(in AUTF8String type, in AUTF8String message, in AUTF8String filename,
|
||||
in uint32_t lineNumber, in uint32_t columnNumber);
|
||||
void callListenerReceivedData(in AUTF8String type, in AUTF8String host, in unsigned short port,
|
||||
[array, size_is(dataLength)] in uint8_t data,
|
||||
in unsigned long dataLength);
|
||||
void callListenerVoid(in AUTF8String type);
|
||||
void callListenerSent(in AUTF8String type, in nsresult status);
|
||||
void updateReadyState(in AUTF8String readyState);
|
||||
};
|
69
dom/network/src/PUDPSocket.ipdl
Normal file
69
dom/network/src/PUDPSocket.ipdl
Normal file
@ -0,0 +1,69 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include protocol PNecko;
|
||||
|
||||
include "mozilla/net/NeckoMessageUtils.h";
|
||||
include "mozilla/net/DNS.h";
|
||||
include "prio.h";
|
||||
|
||||
using mozilla::net::NetAddr from "mozilla/net/DNS.h";
|
||||
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
|
||||
struct UDPError {
|
||||
nsCString message;
|
||||
nsCString filename;
|
||||
uint32_t lineNumber;
|
||||
uint32_t columnNumber;
|
||||
};
|
||||
|
||||
struct UDPMessage {
|
||||
nsCString fromAddr;
|
||||
uint16_t port;
|
||||
uint8_t[] data;
|
||||
};
|
||||
|
||||
struct UDPAddressInfo {
|
||||
nsCString local;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
struct UDPSendResult {
|
||||
nsresult value;
|
||||
};
|
||||
|
||||
union UDPCallbackData {
|
||||
void_t;
|
||||
UDPMessage;
|
||||
UDPAddressInfo;
|
||||
UDPSendResult;
|
||||
UDPError;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
protocol PUDPSocket
|
||||
{
|
||||
manager PNecko;
|
||||
|
||||
parent:
|
||||
Data(uint8_t[] data, nsCString remoteAddress, uint16_t port);
|
||||
DataWithAddress(uint8_t[] data, NetAddr addr);
|
||||
Close();
|
||||
RequestDelete();
|
||||
|
||||
child:
|
||||
Callback(nsCString type, UDPCallbackData data, nsCString aState);
|
||||
__delete__();
|
||||
};
|
||||
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
192
dom/network/src/UDPSocketChild.cpp
Normal file
192
dom/network/src/UDPSocketChild.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "UDPSocketChild.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
|
||||
using mozilla::net::gNeckoChild;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS1(UDPSocketChildBase, nsIUDPSocketChild)
|
||||
|
||||
UDPSocketChildBase::UDPSocketChildBase()
|
||||
: mIPCOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
UDPSocketChildBase::~UDPSocketChildBase()
|
||||
{
|
||||
}
|
||||
void
|
||||
UDPSocketChildBase::ReleaseIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen);
|
||||
mIPCOpen = false;
|
||||
this->Release();
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketChildBase::AddIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(!mIPCOpen);
|
||||
mIPCOpen = true;
|
||||
this->AddRef();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) UDPSocketChild::Release(void)
|
||||
{
|
||||
nsrefcnt refcnt = UDPSocketChildBase::Release();
|
||||
if (refcnt == 1 && mIPCOpen) {
|
||||
PUDPSocketChild::SendRequestDelete();
|
||||
return 1;
|
||||
}
|
||||
return refcnt;
|
||||
}
|
||||
|
||||
UDPSocketChild::UDPSocketChild()
|
||||
:mLocalPort(0)
|
||||
{
|
||||
}
|
||||
|
||||
UDPSocketChild::~UDPSocketChild()
|
||||
{
|
||||
}
|
||||
|
||||
// nsIUDPSocketChild Methods
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::Bind(nsIUDPSocketInternal *aSocket,
|
||||
const nsACString& aHost,
|
||||
uint16_t aPort)
|
||||
{
|
||||
NS_ENSURE_ARG(aSocket);
|
||||
|
||||
mSocket = aSocket;
|
||||
AddIPDLReference();
|
||||
|
||||
gNeckoChild->SendPUDPSocketConstructor(this, nsCString(aHost), aPort);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::Close()
|
||||
{
|
||||
SendClose();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::Send(const nsACString& aHost,
|
||||
uint16_t aPort,
|
||||
const uint8_t *aData,
|
||||
uint32_t aByteLength)
|
||||
{
|
||||
NS_ENSURE_ARG(aData);
|
||||
|
||||
FallibleTArray<uint8_t> fallibleArray;
|
||||
if (!fallibleArray.InsertElementsAt(0, aData, aByteLength)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
InfallibleTArray<uint8_t> array;
|
||||
array.SwapElements(fallibleArray);
|
||||
SendData(array, nsCString(aHost), aPort);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::SendWithAddr(nsINetAddr *aAddr,
|
||||
const uint8_t *aData,
|
||||
uint32_t aByteLength)
|
||||
{
|
||||
NS_ENSURE_ARG(aAddr);
|
||||
NS_ENSURE_ARG(aData);
|
||||
|
||||
NetAddr addr;
|
||||
aAddr->GetNetAddr(&addr);
|
||||
|
||||
return SendWithAddress(&addr, aData, aByteLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::SendWithAddress(const NetAddr *aAddr,
|
||||
const uint8_t *aData,
|
||||
uint32_t aByteLength)
|
||||
{
|
||||
NS_ENSURE_ARG(aAddr);
|
||||
NS_ENSURE_ARG(aData);
|
||||
|
||||
FallibleTArray<uint8_t> fallibleArray;
|
||||
if (!fallibleArray.InsertElementsAt(0, aData, aByteLength)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
InfallibleTArray<uint8_t> array;
|
||||
array.SwapElements(fallibleArray);
|
||||
SendDataWithAddress(array, *aAddr);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::GetLocalPort(uint16_t *aLocalPort)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aLocalPort);
|
||||
|
||||
*aLocalPort = mLocalPort;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::GetLocalAddress(nsACString &aLocalAddress)
|
||||
{
|
||||
aLocalAddress = mLocalAddress;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// PUDPSocketChild Methods
|
||||
bool
|
||||
UDPSocketChild::RecvCallback(const nsCString &aType,
|
||||
const UDPCallbackData &aData,
|
||||
const nsCString &aState)
|
||||
{
|
||||
if (NS_FAILED(mSocket->UpdateReadyState(aState)))
|
||||
NS_ERROR("Shouldn't fail!");
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
if (aData.type() == UDPCallbackData::Tvoid_t) {
|
||||
rv = mSocket->CallListenerVoid(aType);
|
||||
} else if (aData.type() == UDPCallbackData::TUDPError) {
|
||||
const UDPError& err(aData.get_UDPError());
|
||||
rv = mSocket->CallListenerError(aType, err.message(), err.filename(),
|
||||
err.lineNumber(), err.columnNumber());
|
||||
} else if (aData.type() == UDPCallbackData::TUDPMessage) {
|
||||
const UDPMessage& message(aData.get_UDPMessage());
|
||||
InfallibleTArray<uint8_t> data(message.data());
|
||||
rv = mSocket->CallListenerReceivedData(aType, message.fromAddr(), message.port(),
|
||||
data.Elements(), data.Length());
|
||||
} else if (aData.type() == UDPCallbackData::TUDPAddressInfo) {
|
||||
//update local address and port.
|
||||
const UDPAddressInfo& addressInfo(aData.get_UDPAddressInfo());
|
||||
mLocalAddress = addressInfo.local();
|
||||
mLocalPort = addressInfo.port();
|
||||
rv = mSocket->CallListenerVoid(aType);
|
||||
} else if (aData.type() == UDPCallbackData::TUDPSendResult) {
|
||||
const UDPSendResult& returnValue(aData.get_UDPSendResult());
|
||||
rv = mSocket->CallListenerSent(aType, returnValue.value());
|
||||
} else {
|
||||
MOZ_ASSERT("Invalid callback type!");
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
54
dom/network/src/UDPSocketChild.h
Normal file
54
dom/network/src/UDPSocketChild.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_UDPSocketChild_h__
|
||||
#define mozilla_dom_UDPSocketChild_h__
|
||||
|
||||
#include "mozilla/net/PUDPSocketChild.h"
|
||||
#include "nsIUDPSocketChild.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#define UDPSOCKETCHILD_CID \
|
||||
{0xb47e5a0f, 0xd384, 0x48ef, { 0x88, 0x85, 0x42, 0x59, 0x79, 0x3d, 0x9c, 0xf0 }}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class UDPSocketChildBase : public nsIUDPSocketChild {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
protected:
|
||||
UDPSocketChildBase();
|
||||
virtual ~UDPSocketChildBase();
|
||||
nsCOMPtr<nsIUDPSocketInternal> mSocket;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
class UDPSocketChild : public mozilla::net::PUDPSocketChild
|
||||
, public UDPSocketChildBase
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIUDPSOCKETCHILD
|
||||
NS_IMETHOD_(nsrefcnt) Release() MOZ_OVERRIDE;
|
||||
|
||||
UDPSocketChild();
|
||||
virtual ~UDPSocketChild();
|
||||
|
||||
virtual bool RecvCallback(const nsCString& aType,
|
||||
const UDPCallbackData& aData,
|
||||
const nsCString& aState) MOZ_OVERRIDE;
|
||||
private:
|
||||
uint16_t mLocalPort;
|
||||
nsCString mLocalAddress;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // !defined(mozilla_dom_UDPSocketChild_h__)
|
239
dom/network/src/UDPSocketParent.cpp
Normal file
239
dom/network/src/UDPSocketParent.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "UDPSocketParent.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIUDPSocket.h"
|
||||
#include "nsINetAddr.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static void
|
||||
FireInternalError(mozilla::net::PUDPSocketParent *aActor, uint32_t aLineNo)
|
||||
{
|
||||
mozilla::unused <<
|
||||
aActor->SendCallback(NS_LITERAL_CSTRING("onerror"),
|
||||
UDPError(NS_LITERAL_CSTRING("Internal error"),
|
||||
NS_LITERAL_CSTRING(__FILE__), aLineNo, 0),
|
||||
NS_LITERAL_CSTRING("connecting"));
|
||||
}
|
||||
|
||||
static nsresult
|
||||
ConvertNetAddrToString(mozilla::net::NetAddr &netAddr, nsACString *address, uint16_t *port)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(address);
|
||||
NS_ENSURE_ARG_POINTER(port);
|
||||
|
||||
*port = 0;
|
||||
uint32_t bufSize = 0;
|
||||
|
||||
switch(netAddr.raw.family) {
|
||||
case AF_INET:
|
||||
*port = PR_ntohs(netAddr.inet.port);
|
||||
bufSize = mozilla::net::kIPv4CStrBufSize;
|
||||
break;
|
||||
case AF_INET6:
|
||||
*port = PR_ntohs(netAddr.inet6.port);
|
||||
bufSize = mozilla::net::kIPv6CStrBufSize;
|
||||
break;
|
||||
default:
|
||||
//impossible
|
||||
MOZ_ASSERT("Unexpected address family");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
address->SetCapacity(bufSize);
|
||||
NetAddrToString(&netAddr, address->BeginWriting(), bufSize);
|
||||
address->SetLength(strlen(address->BeginReading()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(UDPSocketParent, nsIUDPSocketListener)
|
||||
|
||||
UDPSocketParent::~UDPSocketParent()
|
||||
{
|
||||
}
|
||||
|
||||
// PUDPSocketParent methods
|
||||
|
||||
bool
|
||||
UDPSocketParent::Init(const nsCString &aHost, const uint16_t aPort)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIUDPSocket> sock =
|
||||
do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInternalError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aHost.IsEmpty()) {
|
||||
rv = sock->Init(aPort, false);
|
||||
} else {
|
||||
PRNetAddr prAddr;
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, aPort, &prAddr);
|
||||
PRStatus status = PR_StringToNetAddr(aHost.BeginReading(), &prAddr);
|
||||
if (status != PR_SUCCESS) {
|
||||
FireInternalError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::net::NetAddr addr;
|
||||
PRNetAddrToNetAddr(&prAddr, &addr);
|
||||
rv = sock->InitWithAddress(&addr);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInternalError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
mSocket = sock;
|
||||
|
||||
net::NetAddr localAddr;
|
||||
mSocket->GetAddress(&localAddr);
|
||||
|
||||
uint16_t port;
|
||||
nsCString addr;
|
||||
rv = ConvertNetAddrToString(localAddr, &addr, &port);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
FireInternalError(this, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
// register listener
|
||||
mSocket->AsyncListen(this);
|
||||
mozilla::unused <<
|
||||
PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onopen"),
|
||||
UDPAddressInfo(addr, port),
|
||||
NS_LITERAL_CSTRING("connected"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvData(const InfallibleTArray<uint8_t> &aData,
|
||||
const nsCString& aRemoteAddress,
|
||||
const uint16_t& aPort)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSocket, true);
|
||||
uint32_t count;
|
||||
nsresult rv = mSocket->Send(aRemoteAddress,
|
||||
aPort, aData.Elements(),
|
||||
aData.Length(), &count);
|
||||
mozilla::unused <<
|
||||
PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onsent"),
|
||||
UDPSendResult(rv),
|
||||
NS_LITERAL_CSTRING("connected"));
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
NS_ENSURE_TRUE(count > 0, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvDataWithAddress(const InfallibleTArray<uint8_t>& aData,
|
||||
const mozilla::net::NetAddr& aAddr)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSocket, true);
|
||||
uint32_t count;
|
||||
nsresult rv = mSocket->SendWithAddress(&aAddr, aData.Elements(),
|
||||
aData.Length(), &count);
|
||||
mozilla::unused <<
|
||||
PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onsent"),
|
||||
UDPSendResult(rv),
|
||||
NS_LITERAL_CSTRING("connected"));
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
NS_ENSURE_TRUE(count > 0, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvClose()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSocket, true);
|
||||
nsresult rv = mSocket->Close();
|
||||
mSocket = nullptr;
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UDPSocketParent::RecvRequestDelete()
|
||||
{
|
||||
mozilla::unused << Send__delete__(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
UDPSocketParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen);
|
||||
mIPCOpen = false;
|
||||
if (mSocket) {
|
||||
mSocket->Close();
|
||||
}
|
||||
mSocket = nullptr;
|
||||
}
|
||||
|
||||
// nsIUDPSocketListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage)
|
||||
{
|
||||
// receiving packet from remote host, forward the message content to child process
|
||||
if (!mIPCOpen) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint16_t port;
|
||||
nsCString ip;
|
||||
nsCOMPtr<nsINetAddr> fromAddr;
|
||||
aMessage->GetFromAddr(getter_AddRefs(fromAddr));
|
||||
fromAddr->GetPort(&port);
|
||||
fromAddr->GetAddress(ip);
|
||||
|
||||
nsCString data;
|
||||
aMessage->GetData(data);
|
||||
|
||||
const char* buffer = data.get();
|
||||
uint32_t len = data.Length();
|
||||
|
||||
FallibleTArray<uint8_t> fallibleArray;
|
||||
if (!fallibleArray.InsertElementsAt(0, buffer, len)) {
|
||||
FireInternalError(this, __LINE__);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
InfallibleTArray<uint8_t> infallibleArray;
|
||||
infallibleArray.SwapElements(fallibleArray);
|
||||
|
||||
// compose callback
|
||||
mozilla::unused <<
|
||||
PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("ondata"),
|
||||
UDPMessage(ip, port, infallibleArray),
|
||||
NS_LITERAL_CSTRING("connected"));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketParent::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus)
|
||||
{
|
||||
// underlying socket is dead, send state update to child process
|
||||
if (mIPCOpen) {
|
||||
mozilla::unused <<
|
||||
PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onclose"),
|
||||
mozilla::void_t(),
|
||||
NS_LITERAL_CSTRING("closed"));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
45
dom/network/src/UDPSocketParent.h
Normal file
45
dom/network/src/UDPSocketParent.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_UDPSocketParent_h__
|
||||
#define mozilla_dom_UDPSocketParent_h__
|
||||
|
||||
#include "mozilla/net/PUDPSocketParent.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIUDPSocket.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class UDPSocketParent : public mozilla::net::PUDPSocketParent
|
||||
, public nsIUDPSocketListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIUDPSOCKETLISTENER
|
||||
|
||||
UDPSocketParent() : mIPCOpen(true) {}
|
||||
virtual ~UDPSocketParent();
|
||||
|
||||
bool Init(const nsCString& aHost, const uint16_t aPort);
|
||||
|
||||
virtual bool RecvClose() MOZ_OVERRIDE;
|
||||
virtual bool RecvData(const InfallibleTArray<uint8_t>& aData,
|
||||
const nsCString& aRemoteAddress,
|
||||
const uint16_t& aPort) MOZ_OVERRIDE;
|
||||
virtual bool RecvDataWithAddress( const InfallibleTArray<uint8_t>& data,
|
||||
const mozilla::net::NetAddr& addr);
|
||||
virtual bool RecvRequestDelete() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
||||
|
||||
bool mIPCOpen;
|
||||
nsCOMPtr<nsIUDPSocket> mSocket;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // !defined(mozilla_dom_UDPSocketParent_h__)
|
@ -11,6 +11,8 @@ EXPORTS.mozilla.dom.network += [
|
||||
'TCPSocketChild.h',
|
||||
'TCPSocketParent.h',
|
||||
'Types.h',
|
||||
'UDPSocketChild.h',
|
||||
'UDPSocketParent.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
@ -19,6 +21,8 @@ SOURCES += [
|
||||
'TCPServerSocketParent.cpp',
|
||||
'TCPSocketChild.cpp',
|
||||
'TCPSocketParent.cpp',
|
||||
'UDPSocketChild.cpp',
|
||||
'UDPSocketParent.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
@ -56,6 +60,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
IPDL_SOURCES += [
|
||||
'PTCPServerSocket.ipdl',
|
||||
'PTCPSocket.ipdl',
|
||||
'PUDPSocket.ipdl',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
9
dom/network/tests/unit_ipc/test_udpsocket_ipc.js
Normal file
9
dom/network/tests/unit_ipc/test_udpsocket_ipc.js
Normal file
@ -0,0 +1,9 @@
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function run_test() {
|
||||
Services.prefs.setBoolPref('media.peerconnection.ipc.enabled', true);
|
||||
run_test_in_child("/udpsocket_child.js", function() {
|
||||
Services.prefs.clearUserPref('media.peerconnection.ipc.enabled');
|
||||
do_test_finished();
|
||||
});
|
||||
}
|
164
dom/network/tests/unit_ipc/udpsocket_child.js
Normal file
164
dom/network/tests/unit_ipc/udpsocket_child.js
Normal file
@ -0,0 +1,164 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
'use strict';
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
const SERVER_PORT = 12345;
|
||||
const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0];
|
||||
|
||||
function UDPSocketInternalImpl() {
|
||||
}
|
||||
|
||||
UDPSocketInternalImpl.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUDPSocketInternal]),
|
||||
callListenerError: function(type, message, filename, lineNumber, columnNumber) {
|
||||
if (this.onerror) {
|
||||
this.onerror();
|
||||
} else {
|
||||
do_throw('Received unexpected error: ' + message + ' at ' + filename +
|
||||
':' + lineNumber + ':' + columnNumber);
|
||||
}
|
||||
},
|
||||
callListenerReceivedData: function(type, host, port, data, dataLength) {
|
||||
do_print('*** recv data(' + dataLength + ')=' + data.join() + '\n');
|
||||
if (this.ondata) {
|
||||
try {
|
||||
this.ondata(data, dataLength);
|
||||
} catch(ex) {
|
||||
if (ex === Cr.NS_ERROR_ABORT)
|
||||
throw ex;
|
||||
do_print('Caught exception: ' + ex + '\n' + ex.stack);
|
||||
do_throw('test is broken; bad ondata handler; see above');
|
||||
}
|
||||
} else {
|
||||
do_throw('Received ' + dataLength + ' bytes of unexpected data!');
|
||||
}
|
||||
},
|
||||
callListenerVoid: function(type) {
|
||||
switch (type) {
|
||||
case 'onopen':
|
||||
if (this.onopen) {
|
||||
this.onopen();
|
||||
}
|
||||
break;
|
||||
case 'onclose':
|
||||
if (this.onclose) {
|
||||
this.onclose();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
callListenerSent: function(type, value) {
|
||||
if (value != Cr.NS_OK) {
|
||||
do_throw('Previous send was failed with cause: ' + value);
|
||||
}
|
||||
},
|
||||
updateReadyState: function(readyState) {
|
||||
do_print('*** current state: ' + readyState + '\n');
|
||||
},
|
||||
onopen: function() {},
|
||||
onclose: function() {},
|
||||
};
|
||||
|
||||
function makeSuccessCase(name) {
|
||||
return function() {
|
||||
do_print('got expected: ' + name);
|
||||
run_next_test();
|
||||
};
|
||||
}
|
||||
|
||||
function makeJointSuccess(names) {
|
||||
let funcs = {}, successCount = 0;
|
||||
names.forEach(function(name) {
|
||||
funcs[name] = function() {
|
||||
do_print('got excepted: ' + name);
|
||||
if (++successCount === names.length)
|
||||
run_next_test();
|
||||
};
|
||||
});
|
||||
return funcs;
|
||||
}
|
||||
|
||||
function makeExpectedData(expectedData, callback) {
|
||||
return function(receivedData, receivedDataLength) {
|
||||
if (receivedDataLength != expectedData.length) {
|
||||
do_throw('Received data size mismatched, expected ' + expectedData.length +
|
||||
' but got ' + receivedDataLength);
|
||||
}
|
||||
for (let i = 0; i < receivedDataLength; i++) {
|
||||
if (receivedData[i] != expectedData[i]) {
|
||||
do_throw('Received mismatched data at position ' + i);
|
||||
}
|
||||
}
|
||||
if (callback) {
|
||||
callback();
|
||||
} else {
|
||||
run_next_test();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function makeFailureCase(name) {
|
||||
return function() {
|
||||
let argstr;
|
||||
if (arguments.length) {
|
||||
argstr = '(args: ' +
|
||||
Array.map(arguments, function(x) {return x.data + ""; }).join(" ") + ')';
|
||||
} else {
|
||||
argstr = '(no arguments)';
|
||||
}
|
||||
do_throw('got unexpected: ' + name + ' ' + argstr);
|
||||
};
|
||||
}
|
||||
|
||||
function createSocketChild() {
|
||||
return Cc['@mozilla.org/udp-socket-child;1']
|
||||
.createInstance(Ci.nsIUDPSocketChild);
|
||||
}
|
||||
|
||||
var UDPSocket = createSocketChild();
|
||||
var callback = new UDPSocketInternalImpl();
|
||||
|
||||
function connectSock() {
|
||||
UDPSocket.bind(callback, '127.0.0.1', SERVER_PORT);
|
||||
callback.onopen = makeSuccessCase('open');
|
||||
}
|
||||
|
||||
function sendData() {
|
||||
UDPSocket.send('127.0.0.1', SERVER_PORT, DATA_ARRAY, DATA_ARRAY.length);
|
||||
callback.ondata = makeExpectedData(DATA_ARRAY);
|
||||
}
|
||||
|
||||
function clientClose() {
|
||||
UDPSocket.close();
|
||||
callback.ondata = makeFailureCase('data');
|
||||
callback.onclose = makeSuccessCase('close');
|
||||
}
|
||||
|
||||
function connectError() {
|
||||
UDPSocket = createSocketChild();
|
||||
UDPSocket.bind(callback, 'some non-IP string', SERVER_PORT);
|
||||
callback.onerror = makeSuccessCase('error');
|
||||
callback.onopen = makeFailureCase('open');
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
UDPSocket = null;
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(connectSock);
|
||||
add_test(sendData);
|
||||
add_test(clientClose);
|
||||
add_test(connectError);
|
||||
add_test(cleanup);
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
support-files =
|
||||
udpsocket_child.js
|
||||
|
||||
[test_tcpsocket_ipc.js]
|
||||
[test_tcpserversocket_ipc.js]
|
||||
[test_udpsocket_ipc.js]
|
||||
run-sequentially = Uses hardcoded port, bug 903830.
|
||||
|
@ -1434,7 +1434,7 @@ this.PushService = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._udpServer = Cc["@mozilla.org/network/server-socket-udp;1"]
|
||||
this._udpServer = Cc["@mozilla.org/network/socket-udp;1"]
|
||||
.createInstance(Ci.nsIUDPServerSocket);
|
||||
this._udpServer.init(-1, false);
|
||||
this._udpServer.asyncListen(this);
|
||||
|
@ -479,12 +479,6 @@ AudioManager::SetPhoneState(int32_t aState)
|
||||
obs->NotifyObservers(nullptr, "phone-state-changed", state.get());
|
||||
}
|
||||
|
||||
// follow the switch audio path logic for android, Bug 897364
|
||||
int usage;
|
||||
GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &usage);
|
||||
if (aState == PHONE_STATE_NORMAL && usage == nsIAudioManager::FORCE_BT_SCO) {
|
||||
SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE);
|
||||
}
|
||||
#if ANDROID_VERSION < 17
|
||||
if (AudioSystem::setPhoneState(aState)) {
|
||||
#else
|
||||
|
@ -2389,6 +2389,7 @@ this.GECKO_CARDSTATE_CORPORATE_PUK_REQUIRED = "corporatePukRequired";
|
||||
this.GECKO_CARDSTATE_SERVICE_PROVIDER_PUK_REQUIRED = "serviceProviderPukRequired";
|
||||
this.GECKO_CARDSTATE_SIM_PUK_REQUIRED = "simPersonalizationPukRequired";
|
||||
this.GECKO_CARDSTATE_READY = "ready";
|
||||
this.GECKO_CARDSTATE_PERMANENT_BLOCKED = "permanentBlocked";
|
||||
|
||||
this.GECKO_CARDLOCK_PIN = "pin";
|
||||
this.GECKO_CARDLOCK_PIN2 = "pin2";
|
||||
|
@ -3013,6 +3013,12 @@ let RIL = {
|
||||
newCardState = GECKO_CARDSTATE_UNKNOWN;
|
||||
}
|
||||
|
||||
let pin1State = app.pin1_replaced ? iccStatus.universalPINState :
|
||||
app.pin1;
|
||||
if (pin1State === CARD_PINSTATE_ENABLED_PERM_BLOCKED) {
|
||||
newCardState = GECKO_CARDSTATE_PERMANENT_BLOCKED;
|
||||
}
|
||||
|
||||
if (this.cardState == newCardState) {
|
||||
return;
|
||||
}
|
||||
|
@ -1701,6 +1701,41 @@ add_test(function test_card_app_state() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify permanent blocked for ICC.
|
||||
*/
|
||||
add_test(function test_icc_permanent_blocked() {
|
||||
let worker = newUint8Worker();
|
||||
let ril = worker.RIL;
|
||||
|
||||
function testPermanentBlocked(pin1_replaced, universalPINState, pin1) {
|
||||
let iccStatus = {
|
||||
gsmUmtsSubscriptionAppIndex: 0,
|
||||
universalPINState: universalPINState,
|
||||
apps: [
|
||||
{
|
||||
pin1_replaced: pin1_replaced,
|
||||
pin1: pin1
|
||||
}]
|
||||
};
|
||||
|
||||
ril._processICCStatus(iccStatus);
|
||||
do_check_eq(ril.cardState, GECKO_CARDSTATE_PERMANENT_BLOCKED);
|
||||
}
|
||||
|
||||
testPermanentBlocked(1,
|
||||
CARD_PINSTATE_ENABLED_PERM_BLOCKED,
|
||||
CARD_PINSTATE_UNKNOWN);
|
||||
testPermanentBlocked(1,
|
||||
CARD_PINSTATE_ENABLED_PERM_BLOCKED,
|
||||
CARD_PINSTATE_ENABLED_PERM_BLOCKED);
|
||||
testPermanentBlocked(0,
|
||||
CARD_PINSTATE_UNKNOWN,
|
||||
CARD_PINSTATE_ENABLED_PERM_BLOCKED);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify iccSetCardLock - Facility Lock.
|
||||
*/
|
||||
|
@ -7,21 +7,21 @@
|
||||
[ChromeOnly, Constructor, JSImplementation="@mozilla.org/contactAddress;1"]
|
||||
interface ContactAddress {
|
||||
attribute object? type; // DOMString[]
|
||||
[TreatUndefinedAs=Null] attribute DOMString? streetAddress;
|
||||
[TreatUndefinedAs=Null] attribute DOMString? locality;
|
||||
[TreatUndefinedAs=Null] attribute DOMString? region;
|
||||
[TreatUndefinedAs=Null] attribute DOMString? postalCode;
|
||||
[TreatUndefinedAs=Null] attribute DOMString? countryName;
|
||||
attribute DOMString? streetAddress;
|
||||
attribute DOMString? locality;
|
||||
attribute DOMString? region;
|
||||
attribute DOMString? postalCode;
|
||||
attribute DOMString? countryName;
|
||||
attribute boolean? pref;
|
||||
|
||||
[ChromeOnly]
|
||||
void initialize(optional sequence<DOMString>? type,
|
||||
optional DOMString streetAddress,
|
||||
optional DOMString locality,
|
||||
optional DOMString region,
|
||||
optional DOMString postalCode,
|
||||
optional DOMString countryName,
|
||||
optional boolean pref);
|
||||
optional DOMString? streetAddress,
|
||||
optional DOMString? locality,
|
||||
optional DOMString? region,
|
||||
optional DOMString? postalCode,
|
||||
optional DOMString? countryName,
|
||||
optional boolean? pref);
|
||||
|
||||
object toJSON();
|
||||
};
|
||||
@ -40,13 +40,13 @@ dictionary ContactAddressInit {
|
||||
[ChromeOnly, Constructor, JSImplementation="@mozilla.org/contactField;1"]
|
||||
interface ContactField {
|
||||
attribute object? type; // DOMString[]
|
||||
[TreatUndefinedAs=Null] attribute DOMString? value;
|
||||
attribute DOMString? value;
|
||||
attribute boolean? pref;
|
||||
|
||||
[ChromeOnly]
|
||||
void initialize(optional sequence<DOMString>? type,
|
||||
optional DOMString value,
|
||||
optional boolean pref);
|
||||
optional DOMString? value,
|
||||
optional boolean? pref);
|
||||
|
||||
object toJSON();
|
||||
};
|
||||
@ -60,13 +60,13 @@ dictionary ContactFieldInit {
|
||||
|
||||
[ChromeOnly, Constructor, JSImplementation="@mozilla.org/contactTelField;1"]
|
||||
interface ContactTelField : ContactField {
|
||||
[TreatUndefinedAs=Null] attribute DOMString? carrier;
|
||||
attribute DOMString? carrier;
|
||||
|
||||
[ChromeOnly]
|
||||
void initialize(optional sequence<DOMString>? type,
|
||||
optional DOMString value,
|
||||
optional DOMString? value,
|
||||
optional DOMString? carrier,
|
||||
optional boolean pref);
|
||||
optional boolean? pref);
|
||||
|
||||
object toJSON();
|
||||
};
|
||||
@ -117,8 +117,8 @@ interface mozContact {
|
||||
attribute Date? bday;
|
||||
attribute Date? anniversary;
|
||||
|
||||
[TreatUndefinedAs=Null] attribute DOMString? sex;
|
||||
[TreatUndefinedAs=Null] attribute DOMString? genderIdentity;
|
||||
attribute DOMString? sex;
|
||||
attribute DOMString? genderIdentity;
|
||||
|
||||
attribute object? photo;
|
||||
|
||||
|
@ -30,6 +30,3 @@ _FILES = \
|
||||
|
||||
libs::
|
||||
$(INSTALL) $(_FILES) $(DIST)/bin/res
|
||||
|
||||
install::
|
||||
$(SYSINSTALL) $(IFLAGS1) $(_FILES) $(DESTDIR)$(mozappdir)/res
|
||||
|
@ -11,7 +11,4 @@ DICTIONARY_FILES = $(strip $(wildcard $(LOCALE_SRCDIR)/hunspell/*.dic) $(wildcar
|
||||
ifneq (,$(DICTIONARY_FILES))
|
||||
libs::
|
||||
$(INSTALL) $(DICTIONARY_FILES) $(FINAL_TARGET)/dictionaries
|
||||
|
||||
install::
|
||||
$(SYSINSTALL) $(IFLAGS1) $(DICTIONARY_FILES) $(DESTDIR)$(mozappdir)/dictionaries
|
||||
endif
|
||||
|
@ -214,7 +214,10 @@ void RecordingFontUserDataDestroyFunc(void *aUserData)
|
||||
RecordingFontUserData *userData =
|
||||
static_cast<RecordingFontUserData*>(aUserData);
|
||||
|
||||
// TODO support font in b2g recordings
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
userData->recorder->RecordEvent(RecordedScaledFontDestruction(userData->refPtr));
|
||||
#endif
|
||||
|
||||
delete userData;
|
||||
}
|
||||
@ -227,7 +230,10 @@ DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
|
||||
const GlyphRenderingOptions *aRenderingOptions)
|
||||
{
|
||||
if (!aFont->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()))) {
|
||||
// TODO support font in b2g recordings
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, aFont));
|
||||
#endif
|
||||
RecordingFontUserData *userData = new RecordingFontUserData;
|
||||
userData->refPtr = aFont;
|
||||
userData->recorder = mRecorder;
|
||||
@ -235,7 +241,10 @@ DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
|
||||
&RecordingFontUserDataDestroyFunc);
|
||||
}
|
||||
|
||||
// TODO support font in b2g recordings
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs));
|
||||
#endif
|
||||
mFinalDT->FillGlyphs(aFont, aBuffer, aPattern, aOptions, aRenderingOptions);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,14 @@
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <process.h>
|
||||
#define getpid _getpid
|
||||
#endif
|
||||
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#include "gfxWindowsPlatform.h"
|
||||
@ -319,10 +326,23 @@ int RecordingPrefChanged(const char *aPrefName, void *aClosure)
|
||||
if (prefFileName) {
|
||||
fileName.Append(NS_ConvertUTF16toUTF8(prefFileName));
|
||||
} else {
|
||||
fileName.AssignLiteral("browserrecording.aer");
|
||||
nsCOMPtr<nsIFile> tmpFile;
|
||||
if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) {
|
||||
return 0;
|
||||
}
|
||||
fileName.AppendPrintf("moz2drec_%i_%i.aer", XRE_GetProcessType(), getpid());
|
||||
|
||||
nsresult rv = tmpFile->AppendNative(fileName);
|
||||
if (NS_FAILED(rv))
|
||||
return 0;
|
||||
|
||||
rv = tmpFile->GetNativePath(fileName);
|
||||
if (NS_FAILED(rv))
|
||||
return 0;
|
||||
}
|
||||
|
||||
gPlatform->mRecorder = Factory::CreateEventRecorderForFile(fileName.BeginReading());
|
||||
printf_stderr("Recording to %s\n", fileName.get());
|
||||
Factory::SetGlobalEventRecorder(gPlatform->mRecorder);
|
||||
} else {
|
||||
Factory::SetGlobalEventRecorder(nullptr);
|
||||
|
@ -30,6 +30,3 @@ GARBAGE += \
|
||||
|
||||
libs::
|
||||
$(INSTALL) $(EXPORT_RESOURCE) $(DIST)/bin/res
|
||||
|
||||
install::
|
||||
$(SYSINSTALL) $(IFLAGS1) $(EXPORT_RESOURCE) $(DESTDIR)$(mozappdir)/res
|
||||
|
@ -11,7 +11,4 @@ PATTERN_FILES = $(strip $(wildcard $(srcdir)/*/hyphenation/*.dic))
|
||||
ifneq (,$(PATTERN_FILES))
|
||||
libs::
|
||||
$(INSTALL) $(PATTERN_FILES) $(FINAL_TARGET)/hyphenation
|
||||
|
||||
install::
|
||||
$(SYSINSTALL) $(IFLAGS1) $(PATTERN_FILES) $(DESTDIR)$(mozappdir)/hyphenation
|
||||
endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user