From 70b4a79405667df36e75bb5b0725a0a56ac09615 Mon Sep 17 00:00:00 2001 From: wuggy Date: Sat, 12 Sep 2026 04:29:47 -0700 Subject: [PATCH 1/6] PGO and LTO in clang-cl --- Makefile.in | 10 ++- aclocal.m4 | 1 + build/autoconf/clang-cl-pgo.m4 | 30 +++++++ build/pgo/llvm_pgo.py | 43 ++++++++++ build/pgo/profileserver.py | 28 ++++++- build/pgo/test_llvm_pgo.py | 139 +++++++++++++++++++++++++++++++++ config/config.mk | 16 +++- config/rules.mk | 4 +- js/src/aclocal.m4 | 1 + js/src/old-configure.in | 15 +++- old-configure.in | 15 +++- testing/testsuite-targets.mk | 3 - 12 files changed, 290 insertions(+), 15 deletions(-) create mode 100644 build/autoconf/clang-cl-pgo.m4 create mode 100644 build/pgo/llvm_pgo.py create mode 100644 build/pgo/test_llvm_pgo.py diff --git a/Makefile.in b/Makefile.in index 429bcabab3..4740932549 100644 --- a/Makefile.in +++ b/Makefile.in @@ -220,14 +220,14 @@ ifneq ($(filter-out maybe_clobber_profiledbuild,$(MAKECMDGOALS)),) GARBAGE_DIRS += dist _tests endif -# Windows PGO builds don't perform a clean before the 2nd pass. So, we want +# MSVC PGO builds don't perform a clean before the 2nd pass. So, we want # to preserve content for the 2nd pass on Windows. Everywhere else, we always # process the install manifests as part of export. # For the binaries rule, not all the install manifests matter, so force only # the interesting ones to be done. ifdef MOZ_PROFILE_USE ifndef NO_PROFILE_GUIDED_OPTIMIZE -ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_) +ifneq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__) recurse_pre-export:: install-manifests binaries:: @$(MAKE) install-manifests install_manifests=dist/include @@ -247,6 +247,10 @@ recurse_artifact: $(topsrcdir)/mach --log-no-times artifact install ifndef JS_STANDALONE +# PGO training is also needed in release builds configured without tests. +pgo-profile-run: + $(PYTHON) $(topsrcdir)/build/pgo/profileserver.py $(EXTRA_TEST_ARGS) + ifdef ENABLE_TESTS # Additional makefile targets to call automated test suites include $(topsrcdir)/testing/testsuite-targets.mk @@ -356,7 +360,7 @@ pretty-installer: #XXX: this is a hack, since we don't want to clobber for MSVC # PGO support, but we can't do this test in client.mk -ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_) +ifneq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__) # No point in clobbering if PGO has been explicitly disabled. ifndef NO_PROFILE_GUIDED_OPTIMIZE maybe_clobber_profiledbuild: clean diff --git a/aclocal.m4 b/aclocal.m4 index f14ddbf29c..0e85c6570b 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -17,6 +17,7 @@ builtin(include, build/autoconf/altoptions.m4)dnl builtin(include, build/autoconf/mozprog.m4)dnl builtin(include, build/autoconf/mozheader.m4)dnl builtin(include, build/autoconf/lto.m4)dnl +builtin(include, build/autoconf/clang-cl-pgo.m4)dnl builtin(include, build/autoconf/frameptr.m4)dnl builtin(include, build/autoconf/compiler-opts.m4)dnl builtin(include, build/autoconf/expandlibs.m4)dnl diff --git a/build/autoconf/clang-cl-pgo.m4 b/build/autoconf/clang-cl-pgo.m4 new file mode 100644 index 0000000000..bf4588848f --- /dev/null +++ b/build/autoconf/clang-cl-pgo.m4 @@ -0,0 +1,30 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_CLANG_CL_PGO], [ +if test -n "$CLANG_CL"; then + dnl Override the MSVC/GCC flags: clang-cl uses LLVM instrumentation. + PROFILE_GEN_CFLAGS="-clang:-fprofile-instr-generate" + PROFILE_USE_CFLAGS='-clang:-fprofile-instr-use="$(DEPTH)/merged.profdata"' + PROFILE_GEN_LDFLAGS= + PROFILE_USE_LDFLAGS= + + if test -n "$MOZ_PGO"; then + AC_PATH_PROG(LLVM_PROFDATA, llvm-profdata.exe) + if test -z "$LLVM_PROFDATA"; then + AC_MSG_ERROR([clang-cl PGO requires llvm-profdata from the compiler's LLVM installation on PATH]) + fi + dnl The training harness uses native Windows Python, not an MSYS shell. + LLVM_PROFDATA="$(cd "$(dirname "$LLVM_PROFDATA")" && pwd -W)/$(basename "$LLVM_PROFDATA")" + dnl clang-cl emits a default-library directive for its profile runtime. + dnl We invoke the linker directly, so supply the runtime search path. + clang_resource_dir=`$CC -print-resource-dir | tr '\\' '/'` + if test ! -d "$clang_resource_dir/lib/windows"; then + AC_MSG_ERROR([clang-cl PGO requires the Windows compiler-rt profile runtime]) + fi + PROFILE_GEN_LDFLAGS="-LIBPATH:\"$clang_resource_dir/lib/windows\"" + fi +fi +AC_SUBST(LLVM_PROFDATA) +]) diff --git a/build/pgo/llvm_pgo.py b/build/pgo/llvm_pgo.py new file mode 100644 index 0000000000..f6cff701c7 --- /dev/null +++ b/build/pgo/llvm_pgo.py @@ -0,0 +1,43 @@ +# 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/. + +"""Collect LLVM profiles outside the directories cleaned between PGO passes.""" + +import glob +import os +import subprocess +import tempfile + + +class LLVMProfile(object): + def __init__(self, topobjdir, profdata): + if not profdata or not os.path.isfile(profdata): + raise RuntimeError("clang-cl PGO requires a configured llvm-profdata") + self.topobjdir = os.path.abspath(topobjdir) + self.profdata = profdata + self.output = os.path.join(self.topobjdir, 'merged.profdata') + self.directory = None + + def prepare(self, env): + # Never reuse a previous training run's merged or raw profiles. + if os.path.exists(self.output): + os.remove(self.output) + self.directory = tempfile.mkdtemp(prefix='pgo-profiles-', dir=self.topobjdir) + # %m distinguishes instrumented DLLs; %p distinguishes child processes. + env['LLVM_PROFILE_FILE'] = os.path.join(self.directory, '%m-%p.profraw') + + def merge(self): + profiles = sorted(glob.glob(os.path.join(self.directory, '*.profraw'))) + if not profiles or not any(os.path.getsize(p) for p in profiles): + raise RuntimeError("PGO training produced no LLVM profile data") + # An input list avoids Windows command-line length limits. Keep profiles + # on failure so that corrupt or incompatible data can be diagnosed. + inputs = os.path.join(self.directory, 'profiles.list') + with open(inputs, 'w') as stream: + for profile in profiles: + stream.write(profile + '\n') + subprocess.check_call([self.profdata, 'merge', '-o', self.output, + '-f', inputs]) + if not os.path.isfile(self.output) or not os.path.getsize(self.output): + raise RuntimeError("llvm-profdata did not produce a merged profile") diff --git a/build/pgo/profileserver.py b/build/pgo/profileserver.py index adc93d9b13..c75587ddb3 100644 --- a/build/pgo/profileserver.py +++ b/build/pgo/profileserver.py @@ -18,9 +18,20 @@ import tempfile from datetime import datetime from mozbuild.base import MozbuildObject from buildconfig import substs +from llvm_pgo import LLVMProfile PORT = 8888 + +def wait_for_training(runner, timeout): + result = runner.wait(timeout=timeout) + if result is None: + runner.stop() + raise RuntimeError("PGO training browser timed out after %s seconds" % timeout) + if result != 0: + raise RuntimeError("PGO training browser exited unsuccessfully") + + if __name__ == '__main__': cli = CLI() debug_args, interactive = cli.debugger_arguments() @@ -54,6 +65,15 @@ if __name__ == '__main__': env = os.environ.copy() env["XPCOM_DEBUG_BREAK"] = "warn" + # Keep the training workload in the parent process. In particular, the + # initialization javascript: URL must work without a content subprocess. + # This environment belongs only to the temporary training browser. + env["MOZ_FORCE_DISABLE_E10S"] = "1" + + llvm_profile = None + if substs.get("CLANG_CL") and substs.get("MOZ_PGO"): + llvm_profile = LLVMProfile(build.topobjdir, substs.get("LLVM_PROFDATA")) + llvm_profile.prepare(env) # For VC12+, make sure we can find the right bitness of pgort1x0.dll if not substs.get('HAVE_64BIT_BUILD'): @@ -72,7 +92,7 @@ if __name__ == '__main__': cmdargs=['javascript:Quitter.quit()'], env=env) runner.start() - runner.wait() + wait_for_training(runner, 180) jarlog = os.getenv("JARLOG_FILE") if jarlog: @@ -85,7 +105,9 @@ if __name__ == '__main__': cmdargs=cmdargs, env=env) runner.start(debug_args=debug_args, interactive=interactive) - runner.wait() - httpd.stop() + wait_for_training(runner, 600) + if llvm_profile: + llvm_profile.merge() finally: + httpd.stop() shutil.rmtree(profilePath) diff --git a/build/pgo/test_llvm_pgo.py b/build/pgo/test_llvm_pgo.py new file mode 100644 index 0000000000..782aa34c35 --- /dev/null +++ b/build/pgo/test_llvm_pgo.py @@ -0,0 +1,139 @@ +# 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/. + +import os +import shutil +import subprocess +import sys +import tempfile +import unittest +from distutils.spawn import find_executable + +from llvm_pgo import LLVMProfile + + +class LLVMProfileTest(unittest.TestCase): + def setUp(self): + self.directory = tempfile.mkdtemp(prefix='llvm pgo test ') + self.profile = LLVMProfile(self.directory, sys.executable) + self.check_call = subprocess.check_call + + def tearDown(self): + subprocess.check_call = self.check_call + shutil.rmtree(self.directory) + + def write(self, path, data): + with open(path, 'w') as stream: + stream.write(data) + + def test_missing_tool(self): + with self.assertRaises(RuntimeError): + LLVMProfile(self.directory, None) + + def test_prepare_isolates_runs(self): + self.write(self.profile.output, 'stale merged profile') + env = {} + self.profile.prepare(env) + first = self.profile.directory + self.write(os.path.join(first, 'stale.profraw'), 'stale raw profile') + self.profile.prepare(env) + self.assertNotEqual(first, self.profile.directory) + self.assertFalse(os.path.exists(self.profile.output)) + self.assertEqual(env['LLVM_PROFILE_FILE'], + os.path.join(self.profile.directory, '%m-%p.profraw')) + with self.assertRaises(RuntimeError): + self.profile.merge() + + def test_empty_profiles(self): + self.profile.prepare({}) + self.write(os.path.join(self.profile.directory, 'empty.profraw'), '') + with self.assertRaises(RuntimeError): + self.profile.merge() + + def test_merge_uses_input_list(self): + self.profile.prepare({}) + raw = os.path.join(self.profile.directory, 'module-process.profraw') + self.write(raw, 'raw profile') + def merge(args): + self.assertEqual(args[:4], [sys.executable, 'merge', '-o', + self.profile.output]) + self.assertEqual(args[4], '-f') + with open(args[5]) as stream: + self.assertEqual(stream.read().splitlines(), [raw]) + self.write(self.profile.output, 'merged profile') + subprocess.check_call = merge + self.profile.merge() + + def test_merge_failure_propagates(self): + self.profile.prepare({}) + self.write(os.path.join(self.profile.directory, 'bad.profraw'), 'bad') + def fail(args): + raise subprocess.CalledProcessError(1, args) + subprocess.check_call = fail + with self.assertRaises(subprocess.CalledProcessError): + self.profile.merge() + + def test_merge_requires_output(self): + self.profile.prepare({}) + self.write(os.path.join(self.profile.directory, 'data.profraw'), 'raw') + subprocess.check_call = lambda args: None + with self.assertRaises(RuntimeError): + self.profile.merge() + + +class PGOConfigurationTest(unittest.TestCase): + def setUp(self): + self.make = (os.environ.get('MAKE') or find_executable('mozmake') or + find_executable('make')) + if not self.make: + self.skipTest('GNU make is required') + self.directory = tempfile.mkdtemp(prefix='pgo configuration ') + config = os.path.join(os.path.dirname(__file__), '..', '..', + 'config', 'config.mk') + with open(config) as stream: + source = stream.read() + start = source.index('# Reject stale Windows PGO configuration') + end = source.index('# Enable profile-based feedback', start) + self.makefile = os.path.join(self.directory, 'Makefile') + with open(self.makefile, 'w') as stream: + stream.write(source[start:end] + '\nall:;\n') + + def tearDown(self): + if hasattr(self, 'directory'): + shutil.rmtree(self.directory) + + def run_make(self, *variables): + process = subprocess.Popen([self.make, '-f', self.makefile] + + list(variables), stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + output = process.communicate()[0].decode('utf-8') + return process.returncode, output + + def test_missing_profdata_fails_before_build(self): + code, output = self.run_make('MOZ_PGO=1', 'CLANG_CL=1', 'LLVM_PROFDATA=') + self.assertNotEqual(code, 0) + self.assertIn('requires LLVM_PROFDATA', output) + + def test_msvc_flags_rejected_for_clang(self): + code, output = self.run_make('MOZ_PGO=1', 'CLANG_CL=1', + 'LLVM_PROFDATA=llvm-profdata.exe', + 'PROFILE_GEN_CFLAGS=-GL') + self.assertNotEqual(code, 0) + self.assertIn('requires LLVM instrumentation flags', output) + + def test_llvm_configuration_accepted(self): + code, output = self.run_make('MOZ_PGO=1', 'CLANG_CL=1', + 'LLVM_PROFDATA=llvm-profdata.exe', + 'PROFILE_GEN_CFLAGS=-clang:-fprofile-instr-generate') + self.assertEqual(code, 0, output) + + def test_non_pgo_and_msvc_unaffected(self): + for variables in [('MOZ_PGO=', 'CLANG_CL=1'), + ('MOZ_PGO=1', 'CLANG_CL=')]: + code, output = self.run_make(*variables) + self.assertEqual(code, 0, output) + + +if __name__ == '__main__': + unittest.main() diff --git a/config/config.mk b/config/config.mk index ca04bb25af..c35ff0233b 100644 --- a/config/config.mk +++ b/config/config.mk @@ -210,13 +210,25 @@ ifdef CPP_UNIT_TESTS NO_PROFILE_GUIDED_OPTIMIZE = 1 endif +# Reject stale Windows PGO configuration before compiling the first pass. +ifdef MOZ_PGO +ifdef CLANG_CL +ifeq ($(strip $(LLVM_PROFDATA)),) +$(error clang-cl PGO requires LLVM_PROFDATA; rerun ./mach configure) +endif +ifeq ($(filter -clang:-fprofile-instr-generate,$(PROFILE_GEN_CFLAGS)),) +$(error clang-cl PGO requires LLVM instrumentation flags; rerun ./mach configure) +endif +endif +endif + # Enable profile-based feedback ifneq (1,$(NO_PROFILE_GUIDED_OPTIMIZE)) ifdef MOZ_PROFILE_GENERATE OS_CFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_GEN_CFLAGS)) OS_CXXFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_GEN_CFLAGS)) OS_LDFLAGS += $(PROFILE_GEN_LDFLAGS) -ifeq (WINNT,$(OS_ARCH)) +ifeq ($(OS_ARCH)_$(CLANG_CL),WINNT_) AR_FLAGS += -LTCG endif endif # MOZ_PROFILE_GENERATE @@ -225,7 +237,7 @@ ifdef MOZ_PROFILE_USE OS_CFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_USE_CFLAGS)) OS_CXXFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_USE_CFLAGS)) OS_LDFLAGS += $(PROFILE_USE_LDFLAGS) -ifeq (WINNT,$(OS_ARCH)) +ifeq ($(OS_ARCH)_$(CLANG_CL),WINNT_) AR_FLAGS += -LTCG endif endif # MOZ_PROFILE_USE diff --git a/config/rules.mk b/config/rules.mk index 2078a5160c..f86d8ead20 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -531,7 +531,7 @@ endif ############################################## ifneq (1,$(NO_PROFILE_GUIDED_OPTIMIZE)) ifdef MOZ_PROFILE_USE -ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_) +ifeq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__) # When building with PGO, we have to make sure to re-link # in the MOZ_PROFILE_USE phase if we linked in the # MOZ_PROFILE_GENERATE phase. We'll touch this pgo.relink @@ -563,7 +563,7 @@ endif # MOZ_PROFILE_USE ifdef MOZ_PROFILE_GENERATE # Clean up profiling data during PROFILE_GENERATE phase export:: -ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_) +ifeq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__) $(foreach pgd,$(wildcard *.pgd),pgomgr -clear $(pgd);) else ifdef GNU_CC diff --git a/js/src/aclocal.m4 b/js/src/aclocal.m4 index 22c0911873..495438e15e 100644 --- a/js/src/aclocal.m4 +++ b/js/src/aclocal.m4 @@ -16,6 +16,7 @@ builtin(include, ../../build/autoconf/altoptions.m4)dnl builtin(include, ../../build/autoconf/mozprog.m4)dnl builtin(include, ../../build/autoconf/mozheader.m4)dnl builtin(include, ../../build/autoconf/lto.m4)dnl +builtin(include, ../../build/autoconf/clang-cl-pgo.m4)dnl builtin(include, ../../build/autoconf/frameptr.m4)dnl builtin(include, ../../build/autoconf/compiler-opts.m4)dnl builtin(include, ../../build/autoconf/expandlibs.m4)dnl diff --git a/js/src/old-configure.in b/js/src/old-configure.in index b046699f17..c80d90bd7e 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -711,7 +711,17 @@ case "$target" in if test "$AS_BIN"; then AS="$(basename "$AS_BIN")" fi - AR='lib' + if test -n "$CLANG_CL"; then + dnl Microsoft lib.exe cannot archive LLVM bitcode from ThinLTO. + AC_PATH_PROG(LLVM_LIB, llvm-lib.exe) + if test -z "$LLVM_LIB"; then + AC_MSG_ERROR([clang-cl requires llvm-lib.exe on PATH]) + fi + dnl expandlibs invokes AR from native Windows Python. + AR="$(cd "$(dirname "$LLVM_LIB")" && pwd -W)/$(basename "$LLVM_LIB")" + else + AR='lib' + fi AR_FLAGS='-NOLOGO -OUT:$@' AR_EXTRACT= RANLIB='echo not_ranlib' @@ -2005,6 +2015,9 @@ if test "$ac_cv_struct_tm_zone_tm_gmtoff" = "yes" ; then fi fi # ! SKIP_COMPILER_CHECKS +dnl Windows skips the GCC probes above, but still needs LLVM PGO setup. +MOZ_CLANG_CL_PGO + AC_DEFINE(CPP_THROW_NEW, [throw()]) AC_LANG_C diff --git a/old-configure.in b/old-configure.in index 8744628eae..364027bcee 100644 --- a/old-configure.in +++ b/old-configure.in @@ -876,7 +876,17 @@ case "$target" in if test "$AS_BIN"; then AS="$(basename "$AS_BIN")" fi - AR='lib' + if test -n "$CLANG_CL"; then + dnl Microsoft lib.exe cannot archive LLVM bitcode from ThinLTO. + AC_PATH_PROG(LLVM_LIB, llvm-lib.exe) + if test -z "$LLVM_LIB"; then + AC_MSG_ERROR([clang-cl requires llvm-lib.exe on PATH]) + fi + dnl expandlibs invokes AR from native Windows Python. + AR="$(cd "$(dirname "$LLVM_LIB")" && pwd -W)/$(basename "$LLVM_LIB")" + else + AR='lib' + fi AR_FLAGS='-NOLOGO -OUT:$@' AR_EXTRACT= RANLIB='echo not_ranlib' @@ -4547,6 +4557,9 @@ AC_SUBST(PROFILE_USE_LDFLAGS) fi # ! SKIP_COMPILER_CHECKS +dnl Windows skips the GCC probes above, but still needs LLVM PGO setup. +MOZ_CLANG_CL_PGO + AC_DEFINE(CPP_THROW_NEW, [throw()]) AC_LANG_C diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index f10bd079c2..68f5ff54d8 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -143,9 +143,6 @@ cppunittests-remote: jetpack-tests: cd $(topsrcdir)/addon-sdk/source && $(PYTHON) bin/cfx -b $(abspath $(browser_path)) --parseable testpkgs -pgo-profile-run: - $(PYTHON) $(topsrcdir)/build/pgo/profileserver.py $(EXTRA_TEST_ARGS) - # Package up the tests and test harnesses include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk From b7c0ebfcd4c4a44562342e940048fd3f5bdc6aae Mon Sep 17 00:00:00 2001 From: wuggy Date: Sat, 12 Sep 2026 06:30:01 -0700 Subject: [PATCH 2/6] Optimize string operations with SSE2 --- js/src/jit-test/tests/latin1/sse2-search.js | 21 +++ js/src/jsapi-tests/moz.build | 1 + .../jsapi-tests/testCharacterOperations.cpp | 145 ++++++++++++++++++ js/src/jsstr.cpp | 29 ++-- js/src/vm/CharacterOperations.h | 81 ++++++++++ js/src/vm/String.cpp | 9 +- 6 files changed, 268 insertions(+), 18 deletions(-) create mode 100644 js/src/jit-test/tests/latin1/sse2-search.js create mode 100644 js/src/jsapi-tests/testCharacterOperations.cpp create mode 100644 js/src/vm/CharacterOperations.h diff --git a/js/src/jit-test/tests/latin1/sse2-search.js b/js/src/jit-test/tests/latin1/sse2-search.js new file mode 100644 index 0000000000..064e802b87 --- /dev/null +++ b/js/src/jit-test/tests/latin1/sse2-search.js @@ -0,0 +1,21 @@ +// Exercise vector-sized spans, scalar tails, and mixed character encodings. +for (var length of [0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65]) { + for (var needle of ["\0", "\x7f", "\x80", "\xff", "\u0100", "\ud800", "\uffff"]) { + for (var position = 0; position <= length; ++position) { + var text = "a".repeat(position) + needle + "a".repeat(length - position); + for (var start of [0, position, position + 1, text.length]) { + var expected = start <= position ? position : -1; + assertEq(text.indexOf(needle, start), expected); + assertEq(text.includes(needle, start), expected !== -1); + } + assertEq(text.indexOf(needle + "b"), -1); + assertEq(text.indexOf(needle + "a"), position < length ? position : -1); + } + } + var latin1 = "\0".repeat(length) + "\xff"; + assertEq(latin1.indexOf("\u0100"), -1); // Must not narrow to NUL on x86. + assertEq(latin1.indexOf("\uffff"), -1); // Must not narrow to 0xff on x86. + var wide = "\u0100" + latin1; + assertEq(wide.indexOf("\xff"), length + 1); + assertEq(wide.indexOf(latin1), 1); +} diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 1b8730c49d..f5d32d6c8f 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -17,6 +17,7 @@ UNIFIED_SOURCES += [ 'testBug604087.cpp', 'testCallArgs.cpp', 'testCallNonGenericMethodOnProxy.cpp', + 'testCharacterOperations.cpp', 'testChromeBuffer.cpp', 'testClassGetter.cpp', 'testCloneScript.cpp', diff --git a/js/src/jsapi-tests/testCharacterOperations.cpp b/js/src/jsapi-tests/testCharacterOperations.cpp new file mode 100644 index 0000000000..eb8fdb5ba8 --- /dev/null +++ b/js/src/jsapi-tests/testCharacterOperations.cpp @@ -0,0 +1,145 @@ +/* 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 "vm/CharacterOperations.h" + +#include + +#ifndef JS_CHARACTER_OPERATIONS_STANDALONE +# include "jsapi-tests/tests.h" +# include "jswin.h" +#elif defined(_WIN32) +# include +#endif + +template +static bool +CheckCharacterSearch() +{ + CharT buffer[128]; + const uint32_t needles[] = {0, 0x7f, 0x80, 0xff, 0x100, 0xd800, 0xdc00, 0xffff}; + for (uint32_t value : needles) { + if (sizeof(CharT) == 1 && value > 0xff) + continue; + const CharT needle = CharT(value); + const CharT other = CharT(value ^ 1); + for (size_t offset = 0; offset < 16 / sizeof(CharT); ++offset) { + CharT* text = buffer + offset; + for (size_t length = 0; length <= 96; ++length) { + for (size_t i = 0; i <= length; ++i) + text[i] = other; + // A match just outside the span must never be returned. + text[length] = needle; + if (js::FindCharacter(text, length, needle)) + return false; + for (size_t position = 0; position < length; ++position) { + text[position] = needle; + text[length - 1] = needle; + if (js::FindCharacter(text, length, needle) != text + position) + return false; + text[position] = other; + text[length - 1] = other; + } + } + } + } + return true; +} + +static bool +CheckLatin1Detection() +{ + char16_t buffer[128]; + const char16_t nonLatin1[] = {0x100, 0x8000, 0xd800, 0xdc00, 0xffff}; + for (size_t offset = 0; offset < 8; ++offset) { + char16_t* text = buffer + offset; + for (size_t length = 0; length <= 96; ++length) { + for (size_t i = 0; i < length; ++i) + text[i] = (i & 1) ? 0xff : 0x80; + text[length] = 0xffff; + if (!js::CharactersFitInLatin1(text, length)) + return false; + for (char16_t invalid : nonLatin1) { + for (size_t position = 0; position < length; ++position) { + const char16_t saved = text[position]; + text[position] = invalid; + if (js::CharactersFitInLatin1(text, length)) + return false; + text[position] = saved; + } + } + } + } + return true; +} + +#ifdef _WIN32 +static bool +CheckCharacterPageBoundary() +{ + SYSTEM_INFO info; + GetSystemInfo(&info); + char* pages = static_cast(VirtualAlloc(nullptr, 2 * info.dwPageSize, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); + if (!pages) + return false; + DWORD oldProtection; + char* end = pages + info.dwPageSize; + bool ok = !!VirtualProtect(end, info.dwPageSize, PAGE_NOACCESS, &oldProtection); + if (ok) { + for (size_t length = 0; length <= 64; ++length) { + char* bytes = end - length; + for (size_t i = 0; i < length; ++i) + bytes[i] = 'a'; + ok = ok && !js::FindCharacter(bytes, length, 'b'); + if (length) { + bytes[length - 1] = 'b'; + ok = ok && js::FindCharacter(bytes, length, 'b') == bytes + length - 1; + } + char16_t* wide = reinterpret_cast(end) - length; + for (size_t i = 0; i < length; ++i) + wide[i] = 0xff; + ok = ok && !js::FindCharacter(wide, length, char16_t(0x100)); + ok = ok && js::CharactersFitInLatin1(wide, length); + if (length) { + wide[length - 1] = 0x100; + ok = ok && js::FindCharacter(wide, length, char16_t(0x100)) == wide + length - 1; + ok = ok && !js::CharactersFitInLatin1(wide, length); + } + } + } + VirtualFree(pages, 0, MEM_RELEASE); + return ok; +} +#endif + +static bool +CheckCharacterOperations() +{ + return CheckCharacterSearch() && CheckCharacterSearch() && + CheckCharacterSearch() && CheckLatin1Detection() +#ifdef _WIN32 + && CheckCharacterPageBoundary() +#endif + ; +} + +// Allow testing these native helpers without rebuilding/linking SpiderMonkey. +#ifdef JS_CHARACTER_OPERATIONS_STANDALONE +int main() +{ + if (!CheckCharacterOperations()) { + fprintf(stderr, "Character operation regression test failed\n"); + return 1; + } + return 0; +} +#else +BEGIN_TEST(testCharacterOperations) +{ + CHECK(CheckCharacterOperations()); + return true; +} +END_TEST(testCharacterOperations) +#endif diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 67de5d7236..d8374e8e43 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -38,6 +38,7 @@ #include "js/UniquePtr.h" #include "unicode/uchar.h" #include "unicode/unorm2.h" +#include "vm/CharacterOperations.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" #include "vm/Opcodes.h" @@ -1640,7 +1641,11 @@ FirstCharMatcherUnrolled(const TextChar* text, uint32_t n, const PatChar pat) static const char* FirstCharMatcher8bit(const char* text, uint32_t n, const char pat) { -#if defined(__clang__) +#ifdef JS_HAS_SSE2_CHARACTER_OPERATIONS + if (n >= 16) + return FindCharacter(text, n, pat); +#endif +#if defined(__clang__) return FirstCharMatcherUnrolled(text, n, pat); #else return reinterpret_cast(memchr(text, pat, n)); @@ -1650,6 +1655,10 @@ FirstCharMatcher8bit(const char* text, uint32_t n, const char pat) static const char16_t* FirstCharMatcher16bit(const char16_t* text, uint32_t n, const char16_t pat) { +#ifdef JS_HAS_SSE2_CHARACTER_OPERATIONS + if (n >= 8) + return FindCharacter(text, n, pat); +#endif #if defined(XP_DARWIN) || defined(XP_WIN) /* * Performance of memchr is horrible in OSX. Windows is better, @@ -1734,18 +1743,14 @@ StringMatch(const TextChar* text, uint32_t textLen, const PatChar* pat, uint32_t return -1; #if defined(__i386__) || defined(_M_IX86) || defined(__i386) - /* - * Given enough registers, the unrolled loop below is faster than the - * following loop. 32-bit x86 does not have enough registers. - */ + // Avoid the generic substring matcher for a single character on x86. + // FindCharacter uses SSE2 where available, including mixed encodings. if (patLen == 1) { - const PatChar p0 = *pat; - const TextChar* end = text + textLen; - for (const TextChar* c = text; c != end; ++c) { - if (*c == p0) - return c - text; - } - return -1; + // A two-byte needle cannot match Latin1 text if it exceeds 0xff. + if (sizeof(TextChar) == 1 && uint32_t(*pat) > 0xff) + return -1; + const TextChar* match = FindCharacter(text, textLen, TextChar(*pat)); + return match ? int(match - text) : -1; } #endif diff --git a/js/src/vm/CharacterOperations.h b/js/src/vm/CharacterOperations.h new file mode 100644 index 0000000000..591a3216bf --- /dev/null +++ b/js/src/vm/CharacterOperations.h @@ -0,0 +1,81 @@ +/* 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 vm_CharacterOperations_h +#define vm_CharacterOperations_h + +#include "mozilla/MathAlgorithms.h" + +#include +#include + +// Use intrinsics only when SSE2 is part of the compiler's target baseline. +// Builds for other architectures (or pre-SSE2 x86) retain scalar operations. +#if defined(__SSE2__) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP >= 2) +# define JS_HAS_SSE2_CHARACTER_OPERATIONS +# include +#endif + +namespace js { + +template +inline const CharT* +FindCharacter(const CharT* chars, size_t length, CharT match) +{ + static_assert(sizeof(CharT) == 1 || sizeof(CharT) == 2, "character width"); +#ifdef JS_HAS_SSE2_CHARACTER_OPERATIONS + const size_t lanes = 16 / sizeof(CharT); + if (length >= lanes) { + const __m128i needle = sizeof(CharT) == 1 + ? _mm_set1_epi8(static_cast(match)) + : _mm_set1_epi16(static_cast(match)); + do { + // Never read beyond the supplied span, even at a page boundary. + const __m128i block = _mm_loadu_si128(reinterpret_cast(chars)); + const __m128i equal = sizeof(CharT) == 1 + ? _mm_cmpeq_epi8(block, needle) + : _mm_cmpeq_epi16(block, needle); + const uint32_t mask = static_cast(_mm_movemask_epi8(equal)); + if (mask) + return chars + mozilla::CountTrailingZeroes32(mask) / sizeof(CharT); + chars += lanes; + length -= lanes; + } while (length >= lanes); + } +#endif + for (; length; --length, ++chars) { + if (*chars == match) + return chars; + } + return nullptr; +} + +inline bool +CharactersFitInLatin1(const char16_t* chars, size_t length) +{ +#ifdef JS_HAS_SSE2_CHARACTER_OPERATIONS + if (length >= 8) { + const __m128i highBytes = _mm_set1_epi16(static_cast(0xff00)); + const __m128i zero = _mm_setzero_si128(); + do { + const __m128i block = _mm_loadu_si128(reinterpret_cast(chars)); + const __m128i fits = _mm_cmpeq_epi16(_mm_and_si128(block, highBytes), zero); + if (_mm_movemask_epi8(fits) != 0xffff) + return false; + chars += 8; + length -= 8; + } while (length >= 8); + } +#endif + for (; length; --length, ++chars) { + if (*chars > 0xff) + return false; + } + return true; +} + +} // namespace js + +#endif // vm_CharacterOperations_h diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 538e9c09eb..3ef6fb229d 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -15,6 +15,7 @@ #include "gc/Marking.h" #include "js/UbiNode.h" +#include "vm/CharacterOperations.h" #include "vm/SPSProfiler.h" #include "jscntxtinlines.h" @@ -1147,12 +1148,8 @@ js::NewDependentString(JSContext* cx, JSString* baseArg, size_t start, size_t le static bool CanStoreCharsAsLatin1(const char16_t* s, size_t length) { - for (const char16_t* end = s + length; s < end; ++s) { - if (*s > JSString::MAX_LATIN1_CHAR) - return false; - } - - return true; + static_assert(JSString::MAX_LATIN1_CHAR == 0xff, "Latin1 character range"); + return CharactersFitInLatin1(s, length); } static bool From 25c4eb5efbadee36106a1cab6910b3c740bea8d2 Mon Sep 17 00:00:00 2001 From: wuggy Date: Sat, 12 Sep 2026 08:57:32 -0700 Subject: [PATCH 3/6] [WIP] D3D11 gpu accel --- gfx/layers/d3d11/CompositorD3D11.cpp | 61 ++++++++++ gfx/layers/d3d11/CompositorD3D11.h | 3 + gfx/layers/d3d11/GpuRasterD3D11.c | 84 ++++++++++++++ gfx/layers/d3d11/GpuRasterD3D11.h | 36 ++++++ gfx/layers/moz.build | 1 + gfx/tests/gtest/TestGpuRasterD3D11.cpp | 148 +++++++++++++++++++++++++ gfx/tests/gtest/moz.build | 3 + gfx/thebes/gfxPrefs.h | 1 + modules/libpref/init/all.js | 2 + 9 files changed, 339 insertions(+) create mode 100644 gfx/layers/d3d11/GpuRasterD3D11.c create mode 100644 gfx/layers/d3d11/GpuRasterD3D11.h create mode 100644 gfx/tests/gtest/TestGpuRasterD3D11.cpp diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp index 121794840f..58befb3fca 100644 --- a/gfx/layers/d3d11/CompositorD3D11.cpp +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -7,6 +7,7 @@ #include "TextureD3D11.h" #include "CompositorD3D11Shaders.h" +#include "GpuRasterD3D11.h" #include "gfxWindowsPlatform.h" #include "nsIWidget.h" @@ -143,6 +144,7 @@ private: CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget) : Compositor(aWidget, aParent) + , mGpuRasterViewport(false) , mAttachments(nullptr) , mHwnd(nullptr) , mDisableSequenceForNextFrame(false) @@ -199,6 +201,16 @@ CompositorD3D11::Initialize(nsCString* const out_failureReason) mFeatureLevel = mDevice->GetFeatureLevel(); + D3D11_FEATURE_DATA_D3D11_OPTIONS options = {}; + if (gfxPrefs::D3D11GpuRectangleFills() && + mFeatureLevel >= D3D_FEATURE_LEVEL_10_0 && + SUCCEEDED(mDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, + &options, sizeof(options))) && + options.ClearView) { + mContext->QueryInterface(__uuidof(ID3D11DeviceContext1), + getter_AddRefs(mGpuRasterContext)); + } + mHwnd = mWidget->AsWindows()->GetHwnd(); memset(&mVSConstants, 0, sizeof(VertexShaderConstants)); @@ -656,6 +668,20 @@ CompositorD3D11::GetPSForEffect(Effect* aEffect, MaskType aMaskType) void CompositorD3D11::ClearRect(const gfx::Rect& aRect) { + if (mGpuRasterContext && mGpuRasterViewport && mCurrentRT && + !mCurrentRT->HasComplexProjection()) { + GpuRasterRect rect = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() }; + IntSize size = mCurrentRT->GetSize(); + D3D11_RECT clip = { 0, 0, size.width, size.height }; + const float clear[4] = { 0, 0, 0, 0 }; + if (GpuRasterD3D11FillRect(mGpuRasterContext, mCurrentRT->mRTView, + size.width, size.height, &rect, &clip, clear)) { + // BeginFrame relies on ClearRect to establish premultiplied blending. + mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF); + return; + } + } + mContext->OMSetBlendState(mAttachments->mDisabledBlendState, sBlendFactor, 0xFFFFFFFF); Matrix4x4 identity; @@ -736,6 +762,39 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect, MOZ_ASSERT(mCurrentRT, "No render target"); + // Opaque, integer-aligned translated rectangles can be filled directly by + // the GPU without shader binding or per-quad constant-buffer uploads. + if (mGpuRasterContext && mGpuRasterViewport && !mCurrentRT->HasComplexProjection() && + aEffectChain.mPrimaryEffect->mType == EffectTypes::SOLID_COLOR && + !aEffectChain.mSecondaryEffects[EffectTypes::MASK] && + !aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] && + aOpacity == 1.0f && aTransform.Is2D() && aTransform.As2D().IsTranslation()) { + const Color& color = static_cast(aEffectChain.mPrimaryEffect.get())->mColor; + if (color.a == 1.0f) { + IntPoint origin = mCurrentRT->GetOrigin(); + // Transform both edges in the shader's order before subtracting the + // target origin; moving x/y and then adding width/height can round + // differently for large coordinates. + float left = aRect.x + aTransform._41; + float right = aRect.XMost() + aTransform._41; + float top = aRect.y + aTransform._42; + float bottom = aRect.YMost() + aTransform._42; + GpuRasterRect rect = { left - origin.x, top - origin.y, + right - origin.x, bottom - origin.y }; + IntRect clipRect = aClipRect; + if (mCurrentRT == mDefaultRT) { + clipRect = clipRect.Intersect(mCurrentClip); + } + D3D11_RECT clip = { clipRect.x, clipRect.y, clipRect.XMost(), clipRect.YMost() }; + IntSize size = mCurrentRT->GetSize(); + const float fill[4] = { color.r, color.g, color.b, color.a }; + if (GpuRasterD3D11FillRect(mGpuRasterContext, mCurrentRT->mRTView, + size.width, size.height, &rect, &clip, fill)) { + return; + } + } + } + memcpy(&mVSConstants.layerTransform, &aTransform._11, 64); IntPoint origin = mCurrentRT->GetOrigin(); mVSConstants.renderTargetOffset[0] = origin.x; @@ -1193,6 +1252,7 @@ CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize) projection._33 = 0.0f; PrepareViewport(aSize, projection, 0.0f, 1.0f); + mGpuRasterViewport = true; } void @@ -1213,6 +1273,7 @@ CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize, const gfx::Matrix4x4& aProjection, float aZNear, float aZFar) { + mGpuRasterViewport = false; D3D11_VIEWPORT viewport; viewport.MaxDepth = aZFar; viewport.MinDepth = aZNear; diff --git a/gfx/layers/d3d11/CompositorD3D11.h b/gfx/layers/d3d11/CompositorD3D11.h index ca775cd60b..d26a3d6916 100644 --- a/gfx/layers/d3d11/CompositorD3D11.h +++ b/gfx/layers/d3d11/CompositorD3D11.h @@ -11,6 +11,7 @@ #include "mozilla/layers/Compositor.h" #include "TextureD3D11.h" #include +#include class nsWidget; @@ -174,6 +175,8 @@ private: RefPtr* aOutView); RefPtr mContext; + RefPtr mGpuRasterContext; + bool mGpuRasterViewport; RefPtr mDevice; RefPtr mSwapChain; RefPtr mDefaultRT; diff --git a/gfx/layers/d3d11/GpuRasterD3D11.c b/gfx/layers/d3d11/GpuRasterD3D11.c new file mode 100644 index 0000000000..6e85300502 --- /dev/null +++ b/gfx/layers/d3d11/GpuRasterD3D11.c @@ -0,0 +1,84 @@ +/* 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/. */ + +#define COBJMACROS +#include "GpuRasterD3D11.h" + +#include + +static BOOL +IntegerEdge(double value) +{ + /* Ordered comparisons reject NaNs and infinities before integer conversion. */ + return value >= LONG_MIN && value <= LONG_MAX && value == (LONG)value; +} + +BOOL +GpuRasterD3D11ClipRect(const GpuRasterRect* rect, const D3D11_RECT* clip, + LONG width, LONG height, D3D11_RECT* result) +{ + LONG left, top, right, bottom; + if (!rect || !clip || !result || width <= 0 || height <= 0 || + !IntegerEdge(rect->left) || !IntegerEdge(rect->top) || + !IntegerEdge(rect->right) || !IntegerEdge(rect->bottom) || + rect->right < rect->left || rect->bottom < rect->top) { + return FALSE; + } + + left = (LONG)rect->left; + top = (LONG)rect->top; + right = (LONG)rect->right; + bottom = (LONG)rect->bottom; + if (left < clip->left) left = clip->left; + if (top < clip->top) top = clip->top; + if (right > clip->right) right = clip->right; + if (bottom > clip->bottom) bottom = clip->bottom; + if (left < 0) left = 0; + if (top < 0) top = 0; + if (right > width) right = width; + if (bottom > height) bottom = height; + + if (right <= left || bottom <= top) { + result->left = result->top = result->right = result->bottom = 0; + } else { + result->left = left; + result->top = top; + result->right = right; + result->bottom = bottom; + } + return TRUE; +} + +BOOL +GpuRasterD3D11FillRect(ID3D11DeviceContext1* context, + ID3D11RenderTargetView* target, + LONG width, LONG height, + const GpuRasterRect* rect, const D3D11_RECT* clip, + const float color[4]) +{ + D3D11_RECT clipped; + D3D11_RENDER_TARGET_VIEW_DESC desc; + unsigned i; + if (!context || !target || !color || + !GpuRasterD3D11ClipRect(rect, clip, width, height, &clipped)) { + return FALSE; + } + for (i = 0; i < 4; ++i) { + if (!(color[i] >= 0.0f && color[i] <= 1.0f)) { + return FALSE; + } + } + + ID3D11RenderTargetView_GetDesc(target, &desc); + if ((desc.Format != DXGI_FORMAT_B8G8R8A8_UNORM && + desc.Format != DXGI_FORMAT_R8G8B8A8_UNORM) || + desc.ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2D || + desc.Texture2D.MipSlice != 0) { + return FALSE; + } + if (clipped.right != clipped.left && clipped.bottom != clipped.top) { + ID3D11DeviceContext1_ClearView(context, (ID3D11View*)target, color, &clipped, 1); + } + return TRUE; +} diff --git a/gfx/layers/d3d11/GpuRasterD3D11.h b/gfx/layers/d3d11/GpuRasterD3D11.h new file mode 100644 index 0000000000..ae7c219e69 --- /dev/null +++ b/gfx/layers/d3d11/GpuRasterD3D11.h @@ -0,0 +1,36 @@ +/* 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 GFX_GPU_RASTER_D3D11_H +#define GFX_GPU_RASTER_D3D11_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Device-space edges. Fractional or non-finite edges require normal drawing. */ +typedef struct GpuRasterRect { + double left, top, right, bottom; +} GpuRasterRect; + +/* FALSE means unsupported input; TRUE may produce an empty clipped rectangle. */ +BOOL GpuRasterD3D11ClipRect(const GpuRasterRect* rect, const D3D11_RECT* clip, + LONG width, LONG height, D3D11_RECT* result); + +/* Source replacement, not alpha blending. The caller must check ClearView + * device support and supply the dimensions of the render target. No pipeline + * bindings are changed. FALSE leaves the target untouched for shader fallback. */ +BOOL GpuRasterD3D11FillRect(ID3D11DeviceContext1* context, + ID3D11RenderTargetView* target, + LONG width, LONG height, + const GpuRasterRect* rect, const D3D11_RECT* clip, + const float color[4]); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index dfec86adbf..1b081a780b 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -80,6 +80,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': ] SOURCES += [ 'd3d11/CompositorD3D11.cpp', + 'd3d11/GpuRasterD3D11.c', 'd3d11/ReadbackManagerD3D11.cpp', ] UNIFIED_SOURCES += [ diff --git a/gfx/tests/gtest/TestGpuRasterD3D11.cpp b/gfx/tests/gtest/TestGpuRasterD3D11.cpp new file mode 100644 index 0000000000..27670ecc65 --- /dev/null +++ b/gfx/tests/gtest/TestGpuRasterD3D11.cpp @@ -0,0 +1,148 @@ +/* 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 NOMINMAX +# define NOMINMAX +#endif +#include "d3d11/GpuRasterD3D11.h" + +#include +#include +#include + +#ifndef GPU_RASTER_STANDALONE +# include "gtest/gtest.h" +#endif + +#define CHECK_GPU(condition) do { \ + if (!(condition)) { fprintf(stderr, "GPU rectangle test failed at line %d\n", __LINE__); \ + return false; } \ +} while (0) + +static bool TestGpuRectangleClipping() +{ + D3D11_RECT clip = { 2, 3, 12, 13 }, output; + GpuRasterRect rect = { -4, -5, 20, 21 }; + CHECK_GPU(GpuRasterD3D11ClipRect(&rect, &clip, 10, 10, &output)); + CHECK_GPU(output.left == 2 && output.top == 3 && output.right == 10 && output.bottom == 10); + rect = { 20, 20, 25, 25 }; + CHECK_GPU(GpuRasterD3D11ClipRect(&rect, &clip, 10, 10, &output)); + CHECK_GPU(output.left == output.right && output.top == output.bottom); + rect = { 1.5, 2, 4, 5 }; + CHECK_GPU(!GpuRasterD3D11ClipRect(&rect, &clip, 10, 10, &output)); + rect.left = std::numeric_limits::quiet_NaN(); + CHECK_GPU(!GpuRasterD3D11ClipRect(&rect, &clip, 10, 10, &output)); + rect.left = std::numeric_limits::infinity(); + CHECK_GPU(!GpuRasterD3D11ClipRect(&rect, &clip, 10, 10, &output)); + rect.left = 2147483648.0; + CHECK_GPU(!GpuRasterD3D11ClipRect(&rect, &clip, 10, 10, &output)); + rect = { 6, 2, 4, 5 }; + CHECK_GPU(!GpuRasterD3D11ClipRect(&rect, &clip, 10, 10, &output)); + CHECK_GPU(!GpuRasterD3D11ClipRect(nullptr, &clip, 10, 10, &output)); + CHECK_GPU(!GpuRasterD3D11ClipRect(&rect, &clip, 0, 10, &output)); + return true; +} + +#ifdef GPU_RASTER_STANDALONE +struct GpuRasterTestDevice { + ID3D11Device* device = nullptr; + ID3D11DeviceContext* context = nullptr; + ID3D11DeviceContext1* context1 = nullptr; + ID3D11Texture2D* texture = nullptr; + ID3D11Texture2D* readback = nullptr; + ID3D11RenderTargetView* view = nullptr; + + ~GpuRasterTestDevice() { + if (view) view->Release(); + if (readback) readback->Release(); + if (texture) texture->Release(); + if (context1) context1->Release(); + if (context) context->Release(); + if (device) device->Release(); + } +}; + +static bool TestGpuRectanglePixels(DXGI_FORMAT format) +{ + GpuRasterTestDevice gpu; + CHECK_GPU(SUCCEEDED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, + 0, nullptr, 0, D3D11_SDK_VERSION, + &gpu.device, nullptr, &gpu.context))); + CHECK_GPU(SUCCEEDED(gpu.context->QueryInterface(__uuidof(ID3D11DeviceContext1), + (void**)&gpu.context1))); + D3D11_FEATURE_DATA_D3D11_OPTIONS options = {}; + CHECK_GPU(SUCCEEDED(gpu.device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, + &options, sizeof(options)))); + CHECK_GPU(options.ClearView); + D3D11_TEXTURE2D_DESC desc = {}; + desc.Width = desc.Height = 16; + desc.MipLevels = desc.ArraySize = desc.SampleDesc.Count = 1; + desc.Format = format; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + CHECK_GPU(SUCCEEDED(gpu.device->CreateTexture2D(&desc, nullptr, &gpu.texture))); + CHECK_GPU(SUCCEEDED(gpu.device->CreateRenderTargetView(gpu.texture, nullptr, &gpu.view))); + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + CHECK_GPU(SUCCEEDED(gpu.device->CreateTexture2D(&desc, nullptr, &gpu.readback))); + + const float blue[4] = { 0, 0, 1, 1 }, red[4] = { 1, 0, 0, 1 }, clear[4] = { 0, 0, 0, 0 }; + gpu.context->ClearRenderTargetView(gpu.view, blue); + GpuRasterRect rect = { -3, 2, 12, 20 }; + D3D11_RECT clip = { 3, 4, 10, 11 }; + // ClearView must use the explicit clip, not the context's scissor state. + D3D11_RECT scissor = { 0, 0, 1, 1 }; + gpu.context->RSSetScissorRects(1, &scissor); + gpu.context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST); + CHECK_GPU(GpuRasterD3D11FillRect(gpu.context1, gpu.view, 16, 16, &rect, &clip, red)); + rect = { 5, 5, 7, 7 }; + clip = { 0, 0, 16, 16 }; + CHECK_GPU(GpuRasterD3D11FillRect(gpu.context1, gpu.view, 16, 16, &rect, &clip, clear)); + rect = { 50, 50, 60, 60 }; + CHECK_GPU(GpuRasterD3D11FillRect(gpu.context1, gpu.view, 16, 16, &rect, &clip, clear)); + rect = { 0.5, 0, 16, 16 }; + CHECK_GPU(!GpuRasterD3D11FillRect(gpu.context1, gpu.view, 16, 16, &rect, &clip, clear)); + CHECK_GPU(!GpuRasterD3D11FillRect(nullptr, gpu.view, 16, 16, &rect, &clip, clear)); + + D3D11_PRIMITIVE_TOPOLOGY topology; + gpu.context->IAGetPrimitiveTopology(&topology); + CHECK_GPU(topology == D3D11_PRIMITIVE_TOPOLOGY_POINTLIST); + UINT count = 1; + gpu.context->RSGetScissorRects(&count, &scissor); + CHECK_GPU(count == 1 && scissor.left == 0 && scissor.top == 0 && + scissor.right == 1 && scissor.bottom == 1); + + gpu.context->CopyResource(gpu.readback, gpu.texture); + D3D11_MAPPED_SUBRESOURCE map; + CHECK_GPU(SUCCEEDED(gpu.context->Map(gpu.readback, 0, D3D11_MAP_READ, 0, &map))); + bool equal = true; + for (unsigned y = 0; y < 16; ++y) { + const uint32_t* row = reinterpret_cast( + static_cast(map.pData) + y * map.RowPitch); + for (unsigned x = 0; x < 16; ++x) { + bool isRed = x >= 3 && x < 10 && y >= 4 && y < 11; + uint32_t expected = (isRed == (format == DXGI_FORMAT_R8G8B8A8_UNORM)) + ? 0xff0000ff : 0xffff0000; + if (x >= 5 && x < 7 && y >= 5 && y < 7) expected = 0; + equal = equal && row[x] == expected; + } + } + gpu.context->Unmap(gpu.readback, 0); + CHECK_GPU(equal); + return true; +} + +int main() +{ + return TestGpuRectangleClipping() && + TestGpuRectanglePixels(DXGI_FORMAT_R8G8B8A8_UNORM) && + TestGpuRectanglePixels(DXGI_FORMAT_B8G8R8A8_UNORM) ? 0 : 1; +} +#else +TEST(GpuRasterD3D11, ClipRect) { EXPECT_TRUE(TestGpuRectangleClipping()); } +// WARP/Context1 is not available on every supported Windows installation. +// Run the standalone pixel tests on Windows 8+ with a D3D11.1 runtime. +#endif + +#undef CHECK_GPU diff --git a/gfx/tests/gtest/moz.build b/gfx/tests/gtest/moz.build index 66851abc70..ea1102f71d 100644 --- a/gfx/tests/gtest/moz.build +++ b/gfx/tests/gtest/moz.build @@ -53,6 +53,9 @@ LOCAL_INCLUDES += [ FINAL_LIBRARY = 'xul-gtest' +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows' and CONFIG['MOZ_ENABLE_D3D10_LAYER']: + SOURCES += ['TestGpuRasterD3D11.cpp'] + CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] if CONFIG['GNU_CXX']: diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index eb447c5667..adab38e25b 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -468,6 +468,7 @@ private: DECL_GFX_PREF(Once, "layers.componentalpha.enabled", ComponentAlphaEnabled, bool, true); DECL_GFX_PREF(Live, "layers.composer2d.enabled", Composer2DCompositionEnabled, bool, false); DECL_GFX_PREF(Once, "layers.d3d11.force-warp", LayersD3D11ForceWARP, bool, false); + DECL_GFX_PREF(Once, "layers.d3d11.gpu-rectangle-fills.enabled", D3D11GpuRectangleFills, bool, false); DECL_GFX_PREF(Live, "layers.deaa.enabled", LayersDEAAEnabled, bool, false); DECL_GFX_PREF(Live, "layers.draw-bigimage-borders", DrawBigImageBorders, bool, false); DECL_GFX_PREF(Live, "layers.draw-borders", DrawLayerBorders, bool, false); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 5e124ff937..1df142b5e5 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4570,6 +4570,8 @@ pref("gfx.direct2d.force-enabled", false); pref("layers.prefer-opengl", false); pref("layers.prefer-d3d9", false); +// Experimental C GPU rectangle path. Requires D3D11.1 ClearView support and a restart. +pref("layers.d3d11.gpu-rectangle-fills.enabled", false); // Enable fallback if d3d11 can't be used. See bug #1262187 pref("layers.allow-d3d9-fallback", true); #endif From f317c71c16216b7bb0da62158d7ed0f9f9a73212 Mon Sep 17 00:00:00 2001 From: EAZYBLACK Date: Sat, 12 Sep 2026 19:43:14 +0300 Subject: [PATCH 4/6] Fix build with MSVC 2019+, and New SDK --- build/moz.configure/windows.configure | 17 ++++++++++++++++- memory/mozalloc/mozalloc.h | 6 ++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/build/moz.configure/windows.configure b/build/moz.configure/windows.configure index 4a28470d34..e2cb128265 100644 --- a/build/moz.configure/windows.configure +++ b/build/moz.configure/windows.configure @@ -84,7 +84,14 @@ def get_sdk_dirs(sdk, subdir): include_dirs['winv6.3'] = include_dirs['include'] del include_dirs['include'] - valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True) + def build_num(v): + parts = v.split('.') + try: + return int(parts[2]) if len(parts) >= 3 else 0 + except ValueError: + return 0 + valid_versions = sorted((v for v in set(include_dirs) & set(lib_dirs) + if build_num(v) <= 19041), reverse=True) if valid_versions: return namespace( path=sdk, @@ -136,6 +143,13 @@ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version, 'WINDOWSSDKDIR (%s). Please verify it contains a valid and ' 'complete SDK installation.' % windows_sdk_dir_env) + def sdk_build(path): + parts = os.path.basename(path).split('.') + try: + return int(parts[2]) if len(parts) >= 3 else 0 + except ValueError: + return 0 + sdks = {k: v for k, v in sdks.items() if sdk_build(k) <= 19041} valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) if valid_sdks: biggest_version, sdk = sdks[valid_sdks[0]] @@ -226,6 +240,7 @@ def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env): 'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal ' 'CRT.' % windows_sdk_dir_env) + sdks = {k: v for k, v in sdks.items() if int(str(v[0]).split('.')[2]) <= 19041} valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) if not valid_sdks: raise FatalCheckError('Cannot find the Universal CRT SDK. ' diff --git a/memory/mozalloc/mozalloc.h b/memory/mozalloc/mozalloc.h index de8c549b31..9c5b693288 100644 --- a/memory/mozalloc/mozalloc.h +++ b/memory/mozalloc/mozalloc.h @@ -33,6 +33,12 @@ #define MOZALLOC_HAVE_XMALLOC +/* Workaround build problems with v142+ MSVC*/ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 5043) +#endif + /* Workaround build problem with Sun Studio 12 */ #if defined(__SUNPRO_C) || defined(__SUNPRO_CC) # undef MOZ_MUST_USE From c6ed0457743fb05a7bc65c78bcc1f279bf9473ea Mon Sep 17 00:00:00 2001 From: EAZYBLACK Date: Sat, 12 Sep 2026 17:50:51 +0100 Subject: [PATCH 5/6] Update Benchmark Score --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bce6376429..c9e0e61bc7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Some advantages over upstream, roytam1's Serpent, Eclipse Hydra and Firefox are: - Support for extensions in the Chrome and Edge Web Store (soon) - Support for extensions released for both Pale Moon, Basilisk and Firefox - WebExtensions support comparable to Firefox ESR 78 -- Literally being the fastest XUL browser (33.7 in Speedometer 2.1 compared to 22.4 in Pale Moon 34.2 AVX2) +- Literally being the fastest XUL browser (48.7 in Speedometer 2.1 compared to 22.4 in Pale Moon 34.2 AVX2) - Re-add support for iOS, Android, Windows 2000 and later, NetBSD (soon OpenBSD) ## Interested in running Dactyloidae in Win9x? From 5ec85f3abe28a5d979d74a24cf35a5b8cae3de08 Mon Sep 17 00:00:00 2001 From: EAZYBLACK Date: Sat, 12 Sep 2026 21:58:21 +0300 Subject: [PATCH 6/6] Remove DirectShow as it doesn't work properly. --- dom/media/DecoderTraits.cpp | 38 - dom/media/directshow/AudioSinkFilter.cpp | 290 -------- dom/media/directshow/AudioSinkFilter.h | 96 --- dom/media/directshow/AudioSinkInputPin.cpp | 210 ------ dom/media/directshow/AudioSinkInputPin.h | 78 -- dom/media/directshow/DirectShowDecoder.cpp | 78 -- dom/media/directshow/DirectShowDecoder.h | 45 -- dom/media/directshow/DirectShowReader.cpp | 417 ----------- dom/media/directshow/DirectShowReader.h | 106 --- dom/media/directshow/DirectShowUtils.cpp | 457 ------------ dom/media/directshow/DirectShowUtils.h | 134 ---- dom/media/directshow/SampleSink.cpp | 159 ---- dom/media/directshow/SampleSink.h | 67 -- dom/media/directshow/SourceFilter.cpp | 682 ------------------ dom/media/directshow/SourceFilter.h | 75 -- dom/media/directshow/moz.build | 42 -- dom/media/moz.build | 6 - modules/libpref/init/all.js | 5 - moz.configure | 10 - toolkit/content/aboutSupport.js | 10 - toolkit/library/moz.build | 3 - .../chrome/global/aboutSupport.properties | 2 - toolkit/modules/Troubleshoot.jsm | 3 +- 23 files changed, 1 insertion(+), 3012 deletions(-) delete mode 100644 dom/media/directshow/AudioSinkFilter.cpp delete mode 100644 dom/media/directshow/AudioSinkFilter.h delete mode 100644 dom/media/directshow/AudioSinkInputPin.cpp delete mode 100644 dom/media/directshow/AudioSinkInputPin.h delete mode 100644 dom/media/directshow/DirectShowDecoder.cpp delete mode 100644 dom/media/directshow/DirectShowDecoder.h delete mode 100644 dom/media/directshow/DirectShowReader.cpp delete mode 100644 dom/media/directshow/DirectShowReader.h delete mode 100644 dom/media/directshow/DirectShowUtils.cpp delete mode 100644 dom/media/directshow/DirectShowUtils.h delete mode 100644 dom/media/directshow/SampleSink.cpp delete mode 100644 dom/media/directshow/SampleSink.h delete mode 100644 dom/media/directshow/SourceFilter.cpp delete mode 100644 dom/media/directshow/SourceFilter.h delete mode 100644 dom/media/directshow/moz.build diff --git a/dom/media/DecoderTraits.cpp b/dom/media/DecoderTraits.cpp index 0795830528..891da0e488 100644 --- a/dom/media/DecoderTraits.cpp +++ b/dom/media/DecoderTraits.cpp @@ -34,11 +34,6 @@ #include "FlacDecoder.h" #include "FlacDemuxer.h" -#ifdef MOZ_DIRECTSHOW -#include "DirectShowDecoder.h" -#include "DirectShowReader.h" -#endif - #include "nsPluginHost.h" #include "MediaPrefs.h" @@ -143,14 +138,6 @@ IsFlacSupportedType(const nsACString& aType, return FlacDecoder::CanHandleMediaType(aType, aCodecs); } -#ifdef MOZ_DIRECTSHOW -static bool -IsDirectShowSupportedType(const nsACString& aType) -{ - return DirectShowDecoder::GetSupportedCodecs(aType, nullptr); -} -#endif - static CanPlayStatus CanHandleCodecsType(const MediaContentType& aType, @@ -210,11 +197,6 @@ CanHandleCodecsType(const MediaContentType& aType, if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) { return CANPLAY_YES; } -#ifdef MOZ_DIRECTSHOW - if (IsDirectShowSupportedType(aType.GetMIMEType())) { - return CANPLAY_YES; - } -#endif if (!codecList) { return CANPLAY_MAYBE; } @@ -276,11 +258,6 @@ CanHandleMediaType(const MediaContentType& aType, if (IsFlacSupportedType(aType.GetMIMEType())) { return CANPLAY_MAYBE; } -#ifdef MOZ_DIRECTSHOW - if (IsDirectShowSupportedType(aType.GetMIMEType())) { - return CANPLAY_MAYBE; - } -#endif return CANPLAY_NO; } @@ -370,13 +347,6 @@ InstantiateDecoder(const nsACString& aType, return decoder.forget(); } -#ifdef MOZ_DIRECTSHOW - if (IsDirectShowSupportedType(aType)) { - decoder = new DirectShowDecoder(aOwner); - return decoder.forget(); - } -#endif - return nullptr; } @@ -423,11 +393,6 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac decoderReader = new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource())); } -#ifdef MOZ_DIRECTSHOW - else if (IsDirectShowSupportedType(aType)) { - decoderReader = new DirectShowReader(aDecoder); - } -#endif return decoderReader; } @@ -453,9 +418,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType) IsAACSupportedType(aType) || IsWaveSupportedType(aType) || IsFlacSupportedType(aType) || -#ifdef MOZ_DIRECTSHOW - IsDirectShowSupportedType(aType) || -#endif false; } diff --git a/dom/media/directshow/AudioSinkFilter.cpp b/dom/media/directshow/AudioSinkFilter.cpp deleted file mode 100644 index 58a329338f..0000000000 --- a/dom/media/directshow/AudioSinkFilter.cpp +++ /dev/null @@ -1,290 +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/. */ - -#include "SampleSink.h" -#include "AudioSinkFilter.h" -#include "AudioSinkInputPin.h" -#include "VideoUtils.h" -#include "mozilla/Logging.h" - - -#include -#include - -#define DELETE_RESET(p) { delete (p) ; (p) = nullptr ;} - -DEFINE_GUID(CLSID_MozAudioSinkFilter, 0x1872d8c8, 0xea8d, 0x4c34, 0xae, 0x96, 0x69, 0xde, - 0xf1, 0x33, 0x7b, 0x33); - -using namespace mozilla::media; - -namespace mozilla { - -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -AudioSinkFilter::AudioSinkFilter(const wchar_t* aObjectName, HRESULT* aOutResult) - : BaseFilter(aObjectName, CLSID_MozAudioSinkFilter), - mFilterCritSec("AudioSinkFilter::mFilterCritSec") -{ - (*aOutResult) = S_OK; - mInputPin = new AudioSinkInputPin(L"AudioSinkInputPin", - this, - &mFilterCritSec, - aOutResult); -} - -AudioSinkFilter::~AudioSinkFilter() -{ -} - -int -AudioSinkFilter::GetPinCount() -{ - return 1; -} - -BasePin* -AudioSinkFilter::GetPin(int aIndex) -{ - CriticalSectionAutoEnter lockFilter(mFilterCritSec); - return (aIndex == 0) ? static_cast(mInputPin) : nullptr; -} - -HRESULT -AudioSinkFilter::Pause() -{ - CriticalSectionAutoEnter lockFilter(mFilterCritSec); - if (mState == State_Stopped) { - // Change the state, THEN activate the input pin. - mState = State_Paused; - if (mInputPin && mInputPin->IsConnected()) { - mInputPin->Active(); - } - } else if (mState == State_Running) { - mState = State_Paused; - } - return S_OK; -} - -HRESULT -AudioSinkFilter::Stop() -{ - CriticalSectionAutoEnter lockFilter(mFilterCritSec); - mState = State_Stopped; - if (mInputPin) { - mInputPin->Inactive(); - } - - GetSampleSink()->Flush(); - - return S_OK; -} - -HRESULT -AudioSinkFilter::Run(REFERENCE_TIME tStart) -{ - LOG("AudioSinkFilter::Run(%lld) [%4.2lf]", - RefTimeToUsecs(tStart), - double(RefTimeToUsecs(tStart)) / USECS_PER_S); - return media::BaseFilter::Run(tStart); -} - -HRESULT -AudioSinkFilter::GetClassID( OUT CLSID * pCLSID ) -{ - (* pCLSID) = CLSID_MozAudioSinkFilter; - return S_OK; -} - -HRESULT -AudioSinkFilter::QueryInterface(REFIID aIId, void **aInterface) -{ - if (aIId == IID_IMediaSeeking) { - *aInterface = static_cast(this); - AddRef(); - return S_OK; - } - return mozilla::media::BaseFilter::QueryInterface(aIId, aInterface); -} - -ULONG -AudioSinkFilter::AddRef() -{ - return ::InterlockedIncrement(&mRefCnt); -} - -ULONG -AudioSinkFilter::Release() -{ - unsigned long newRefCnt = ::InterlockedDecrement(&mRefCnt); - if (!newRefCnt) { - delete this; - } - return newRefCnt; -} - -SampleSink* -AudioSinkFilter::GetSampleSink() -{ - return mInputPin->GetSampleSink(); -} - -const ::VIDEOINFOHEADER* -AudioSinkFilter::GetVideoInfo() const -{ - return mInputPin->GetVideoInfo(); -} - - -// IMediaSeeking implementation. -// -// Calls to IMediaSeeking are forwarded to the output pin that the -// AudioSinkInputPin is connected to, i.e. upstream towards the parser and -// source filters, which actually implement seeking. -#define ENSURE_CONNECTED_PIN_SEEKING \ - if (!mInputPin) { \ - return E_NOTIMPL; \ - } \ - RefPtr pinSeeking = mInputPin->GetConnectedPinSeeking(); \ - if (!pinSeeking) { \ - return E_NOTIMPL; \ - } - -HRESULT -AudioSinkFilter::GetCapabilities(DWORD* aCapabilities) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetCapabilities(aCapabilities); -} - -HRESULT -AudioSinkFilter::CheckCapabilities(DWORD* aCapabilities) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->CheckCapabilities(aCapabilities); -} - -HRESULT -AudioSinkFilter::IsFormatSupported(const GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->IsFormatSupported(aFormat); -} - -HRESULT -AudioSinkFilter::QueryPreferredFormat(GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->QueryPreferredFormat(aFormat); -} - -HRESULT -AudioSinkFilter::GetTimeFormat(GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetTimeFormat(aFormat); -} - -HRESULT -AudioSinkFilter::IsUsingTimeFormat(const GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->IsUsingTimeFormat(aFormat); -} - -HRESULT -AudioSinkFilter::SetTimeFormat(const GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->SetTimeFormat(aFormat); -} - -HRESULT -AudioSinkFilter::GetDuration(LONGLONG* aDuration) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetDuration(aDuration); -} - -HRESULT -AudioSinkFilter::GetStopPosition(LONGLONG* aStop) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetStopPosition(aStop); -} - -HRESULT -AudioSinkFilter::GetCurrentPosition(LONGLONG* aCurrent) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetCurrentPosition(aCurrent); -} - -HRESULT -AudioSinkFilter::ConvertTimeFormat(LONGLONG* aTarget, - const GUID* aTargetFormat, - LONGLONG aSource, - const GUID* aSourceFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->ConvertTimeFormat(aTarget, - aTargetFormat, - aSource, - aSourceFormat); -} - -HRESULT -AudioSinkFilter::SetPositions(LONGLONG* aCurrent, - DWORD aCurrentFlags, - LONGLONG* aStop, - DWORD aStopFlags) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->SetPositions(aCurrent, - aCurrentFlags, - aStop, - aStopFlags); -} - -HRESULT -AudioSinkFilter::GetPositions(LONGLONG* aCurrent, - LONGLONG* aStop) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetPositions(aCurrent, aStop); -} - -HRESULT -AudioSinkFilter::GetAvailable(LONGLONG* aEarliest, - LONGLONG* aLatest) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetAvailable(aEarliest, aLatest); -} - -HRESULT -AudioSinkFilter::SetRate(double aRate) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->SetRate(aRate); -} - -HRESULT -AudioSinkFilter::GetRate(double* aRate) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetRate(aRate); -} - -HRESULT -AudioSinkFilter::GetPreroll(LONGLONG* aPreroll) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetPreroll(aPreroll); -} - -} // namespace mozilla diff --git a/dom/media/directshow/AudioSinkFilter.h b/dom/media/directshow/AudioSinkFilter.h deleted file mode 100644 index 2170d45f00..0000000000 --- a/dom/media/directshow/AudioSinkFilter.h +++ /dev/null @@ -1,96 +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/. */ - -#if !defined(AudioSinkFilter_h_) -#define AudioSinkFilter_h_ - -#include "BaseFilter.h" -#include "DirectShowUtils.h" -#include "nsAutoPtr.h" -#include "mozilla/RefPtr.h" - -namespace mozilla { - -class AudioSinkInputPin; -class SampleSink; - -// Filter that acts as the end of the graph. Audio samples input into -// this filter block the calling thread, and the calling thread is -// unblocked when the decode thread extracts the sample. The samples -// input into this filter are stored in the SampleSink, where the blocking -// is implemented. The input pin owns the SampleSink. -class AudioSinkFilter: public mozilla::media::BaseFilter, - public IMediaSeeking -{ - -public: - AudioSinkFilter(const wchar_t* aObjectName, HRESULT* aOutResult); - virtual ~AudioSinkFilter(); - - // Gets the input pin's sample sink. - SampleSink* GetSampleSink(); - const ::VIDEOINFOHEADER* GetVideoInfo() const; - - // IUnknown implementation. - STDMETHODIMP QueryInterface(REFIID aIId, void **aInterface); - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); - - // -------------------------------------------------------------------- - // CBaseFilter methods - int GetPinCount (); - mozilla::media::BasePin* GetPin ( IN int Index); - STDMETHODIMP Pause (); - STDMETHODIMP Stop (); - STDMETHODIMP GetClassID ( OUT CLSID * pCLSID); - STDMETHODIMP Run(REFERENCE_TIME tStart); - // IMediaSeeking Methods... - - // We defer to SourceFilter, but we must expose the interface on - // the output pins. Seeking commands come upstream from the renderers, - // but they must be actioned at the source filters. - STDMETHODIMP GetCapabilities(DWORD* aCapabilities); - STDMETHODIMP CheckCapabilities(DWORD* aCapabilities); - STDMETHODIMP IsFormatSupported(const GUID* aFormat); - STDMETHODIMP QueryPreferredFormat(GUID* aFormat); - STDMETHODIMP GetTimeFormat(GUID* aFormat); - STDMETHODIMP IsUsingTimeFormat(const GUID* aFormat); - STDMETHODIMP SetTimeFormat(const GUID* aFormat); - STDMETHODIMP GetDuration(LONGLONG* pDuration); - STDMETHODIMP GetStopPosition(LONGLONG* pStop); - STDMETHODIMP GetCurrentPosition(LONGLONG* aCurrent); - STDMETHODIMP ConvertTimeFormat(LONGLONG* aTarget, - const GUID* aTargetFormat, - LONGLONG aSource, - const GUID* aSourceFormat); - STDMETHODIMP SetPositions(LONGLONG* aCurrent, - DWORD aCurrentFlags, - LONGLONG* aStop, - DWORD aStopFlags); - STDMETHODIMP GetPositions(LONGLONG* aCurrent, - LONGLONG* aStop); - STDMETHODIMP GetAvailable(LONGLONG* aEarliest, - LONGLONG* aLatest); - STDMETHODIMP SetRate(double aRate); - STDMETHODIMP GetRate(double* aRate); - STDMETHODIMP GetPreroll(LONGLONG* aPreroll); - - // -------------------------------------------------------------------- - // class factory calls this - static IUnknown * CreateInstance (IN LPUNKNOWN punk, OUT HRESULT * phr); - -private: - CriticalSection mFilterCritSec; - - // Note: The input pin defers its refcounting to the sink filter, so when - // the input pin is addrefed, what actually happens is the sink filter is - // addrefed. - nsAutoPtr mInputPin; -}; - -} // namespace mozilla - -#endif // AudioSinkFilter_h_ diff --git a/dom/media/directshow/AudioSinkInputPin.cpp b/dom/media/directshow/AudioSinkInputPin.cpp deleted file mode 100644 index c1ff9d55ea..0000000000 --- a/dom/media/directshow/AudioSinkInputPin.cpp +++ /dev/null @@ -1,210 +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/. */ - -#include "AudioSinkInputPin.h" -#include "AudioSinkFilter.h" -#include "SampleSink.h" -#include "mozilla/Logging.h" - -#include - -using namespace mozilla::media; - -namespace mozilla { - -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -AudioSinkInputPin::AudioSinkInputPin(wchar_t* aObjectName, - AudioSinkFilter* aFilter, - mozilla::CriticalSection* aLock, - HRESULT* aOutResult) - : BaseInputPin(aObjectName, aFilter, aLock, aOutResult, aObjectName), - mSegmentStartTime(0) -{ - MOZ_COUNT_CTOR(AudioSinkInputPin); - mSampleSink = new SampleSink(); - memset(&mVideoInfo, 0, sizeof(mVideoInfo)); -} - -AudioSinkInputPin::~AudioSinkInputPin() -{ - MOZ_COUNT_DTOR(AudioSinkInputPin); -} - -HRESULT -AudioSinkInputPin::GetMediaType(int aPosition, MediaType* aOutMediaType) -{ - NS_ENSURE_TRUE(aPosition >= 0, E_INVALIDARG); - NS_ENSURE_TRUE(aOutMediaType, E_POINTER); - - // Note: We set output as PCM, as IEEE_FLOAT only works when using the - // MP3 decoder as an MFT, and we can't do that while using DirectShow. - aOutMediaType->SetType(&MEDIATYPE_Audio); - aOutMediaType->SetSubtype(&MEDIASUBTYPE_PCM); - aOutMediaType->SetFormatType(&FORMAT_WaveFormatEx); - aOutMediaType->SetTemporalCompression(FALSE); - - if (aPosition == 0) { - return S_OK; - } - if (aPosition == 1) { - aOutMediaType->SetType(&MEDIATYPE_Video); - aOutMediaType->SetSubtype(&MEDIASUBTYPE_YUY2); - aOutMediaType->SetFormatType(&FORMAT_VideoInfo); - aOutMediaType->SetTemporalCompression(FALSE); - return S_OK; - } - return S_FALSE; -} - -HRESULT -AudioSinkInputPin::CheckMediaType(const MediaType* aMediaType) -{ - if (!aMediaType) { - return E_INVALIDARG; - } - - GUID majorType = *aMediaType->Type(); - if (majorType == MEDIATYPE_Video && - *aMediaType->Subtype() == MEDIASUBTYPE_YUY2 && - *aMediaType->FormatType() == FORMAT_VideoInfo) { - if (aMediaType->cbFormat >= sizeof(VIDEOINFOHEADER)) { - memcpy(&mVideoInfo, aMediaType->pbFormat, sizeof(VIDEOINFOHEADER)); - } - return S_OK; - } - - if (majorType != MEDIATYPE_Audio && majorType != WMMEDIATYPE_Audio) { - return E_INVALIDARG; - } - - if (*aMediaType->Subtype() != MEDIASUBTYPE_PCM) { - return E_INVALIDARG; - } - - if (*aMediaType->FormatType() != FORMAT_WaveFormatEx) { - return E_INVALIDARG; - } - - // We accept the media type, stash its layout format! - WAVEFORMATEX* wfx = (WAVEFORMATEX*)(aMediaType->pbFormat); - GetSampleSink()->SetAudioFormat(wfx); - - return S_OK; -} - -AudioSinkFilter* -AudioSinkInputPin::GetAudioSinkFilter() -{ - return reinterpret_cast(mFilter); -} - -SampleSink* -AudioSinkInputPin::GetSampleSink() -{ - return mSampleSink; -} - -HRESULT -AudioSinkInputPin::SetAbsoluteMediaTime(IMediaSample* aSample) -{ - HRESULT hr; - REFERENCE_TIME start = 0, end = 0; - hr = aSample->GetTime(&start, &end); - NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL); - { - CriticalSectionAutoEnter lock(*mLock); - start += mSegmentStartTime; - end += mSegmentStartTime; - } - hr = aSample->SetMediaTime(&start, &end); - NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL); - return S_OK; -} - -HRESULT -AudioSinkInputPin::Receive(IMediaSample* aSample ) -{ - HRESULT hr; - NS_ENSURE_TRUE(aSample, E_POINTER); - - hr = BaseInputPin::Receive(aSample); - if (SUCCEEDED(hr) && hr != S_FALSE) { // S_FALSE == flushing - // Set the timestamp of the sample after being adjusted for - // seeking/segments in the "media time" attribute. When we seek, - // DirectShow starts a new "segment", and starts labeling samples - // from time=0 again, so we need to correct for this to get the - // actual timestamps after seeking. - hr = SetAbsoluteMediaTime(aSample); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - hr = GetSampleSink()->Receive(aSample); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - } - return S_OK; -} - -already_AddRefed -AudioSinkInputPin::GetConnectedPinSeeking() -{ - RefPtr peer = GetConnected(); - if (!peer) - return nullptr; - RefPtr seeking; - peer->QueryInterface(static_cast(getter_AddRefs(seeking))); - return seeking.forget(); -} - -HRESULT -AudioSinkInputPin::BeginFlush() -{ - HRESULT hr = media::BaseInputPin::BeginFlush(); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - GetSampleSink()->Flush(); - - return S_OK; -} - -HRESULT -AudioSinkInputPin::EndFlush() -{ - HRESULT hr = media::BaseInputPin::EndFlush(); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - // Reset the EOS flag, so that if we're called after a seek we still work. - GetSampleSink()->Reset(); - - return S_OK; -} - -HRESULT -AudioSinkInputPin::EndOfStream(void) -{ - HRESULT hr = media::BaseInputPin::EndOfStream(); - if (FAILED(hr) || hr == S_FALSE) { - // Pin is stil flushing. - return hr; - } - GetSampleSink()->SetEOS(); - - return S_OK; -} - - -HRESULT -AudioSinkInputPin::NewSegment(REFERENCE_TIME tStart, - REFERENCE_TIME tStop, - double dRate) -{ - CriticalSectionAutoEnter lock(*mLock); - // Record the start time of the new segment, so that we can store the - // correct absolute timestamp in the "media time" each incoming sample. - mSegmentStartTime = tStart; - return S_OK; -} - -} // namespace mozilla diff --git a/dom/media/directshow/AudioSinkInputPin.h b/dom/media/directshow/AudioSinkInputPin.h deleted file mode 100644 index 17650774f0..0000000000 --- a/dom/media/directshow/AudioSinkInputPin.h +++ /dev/null @@ -1,78 +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/. */ - -#if !defined(AudioSinkInputPin_h_) -#define AudioSinkInputPin_h_ - -#include "BaseInputPin.h" -#include "DirectShowUtils.h" -#include "mozilla/RefPtr.h" -#include "nsAutoPtr.h" - -namespace mozilla { - -namespace media { - class MediaType; -} - -class AudioSinkFilter; -class SampleSink; - - -// Input pin for capturing audio output of a DirectShow filter graph. -// This is the input pin for the AudioSinkFilter. -class AudioSinkInputPin: public mozilla::media::BaseInputPin -{ -public: - AudioSinkInputPin(wchar_t* aObjectName, - AudioSinkFilter* aFilter, - mozilla::CriticalSection* aLock, - HRESULT* aOutResult); - virtual ~AudioSinkInputPin(); - - HRESULT GetMediaType (IN int iPos, OUT mozilla::media::MediaType * pmt); - HRESULT CheckMediaType (IN const mozilla::media::MediaType * pmt); - STDMETHODIMP Receive (IN IMediaSample *); - STDMETHODIMP BeginFlush() override; - STDMETHODIMP EndFlush() override; - - // Called when we start decoding a new segment, that happens directly after - // a seek. This captures the segment's start time. Samples decoded by the - // MP3 decoder have their timestamps offset from the segment start time. - // Storing the segment start time enables us to set each sample's MediaTime - // as an offset in the stream relative to the start of the stream, rather - // than the start of the segment, i.e. its absolute time in the stream. - STDMETHODIMP NewSegment(REFERENCE_TIME tStart, - REFERENCE_TIME tStop, - double dRate) override; - - STDMETHODIMP EndOfStream() override; - - // Returns the IMediaSeeking interface of the connected output pin. - // We forward seeking requests upstream from the sink to the source - // filters. - already_AddRefed GetConnectedPinSeeking(); - - SampleSink* GetSampleSink(); - const VIDEOINFOHEADER* GetVideoInfo() const { return &mVideoInfo; } - -private: - AudioSinkFilter* GetAudioSinkFilter(); - - // Sets the media time on the media sample, relative to the segment - // start time. - HRESULT SetAbsoluteMediaTime(IMediaSample* aSample); - - nsAutoPtr mSampleSink; - - // Synchronized by the filter lock; BaseInputPin::mLock. - REFERENCE_TIME mSegmentStartTime; - VIDEOINFOHEADER mVideoInfo; -}; - -} // namespace mozilla - -#endif // AudioSinkInputPin_h_ diff --git a/dom/media/directshow/DirectShowDecoder.cpp b/dom/media/directshow/DirectShowDecoder.cpp deleted file mode 100644 index d10c850e2f..0000000000 --- a/dom/media/directshow/DirectShowDecoder.cpp +++ /dev/null @@ -1,78 +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/. */ - -#include "DirectShowDecoder.h" -#include "DirectShowReader.h" -#include "DirectShowUtils.h" -#include "MediaDecoderStateMachine.h" -#include "mozilla/Preferences.h" -#include "mozilla/WindowsVersion.h" - -namespace mozilla { - -MediaDecoderStateMachine* DirectShowDecoder::CreateStateMachine() -{ - return new MediaDecoderStateMachine(this, new DirectShowReader(this)); -} - -/* static */ -bool -DirectShowDecoder::GetSupportedCodecs(const nsACString& aType, - char const *const ** aCodecList) -{ - if (!IsEnabled()) { - return false; - } - - static char const *const mp3AudioCodecs[] = { - "mp3", - nullptr - }; - if (aType.EqualsASCII("audio/mpeg") || - aType.EqualsASCII("audio/mp3")) { - if (aCodecList) { - *aCodecList = mp3AudioCodecs; - } - return true; - } - - static char const *const h264VideoCodecs[] = { - "avc1", - "h264", - nullptr - }; - if ((aType.EqualsASCII("video/h264") || - aType.EqualsASCII("video/avc")) && - CanDecodeH264UsingDirectShow()) { - if (aCodecList) { - *aCodecList = h264VideoCodecs; - } - return true; - } - - return false; -} - -/* static */ -bool -DirectShowDecoder::IsEnabled() -{ - return Preferences::GetBool("media.directshow.enabled") && - (CanDecodeMP3UsingDirectShow() || CanDecodeH264UsingDirectShow()); -} - -DirectShowDecoder::DirectShowDecoder(MediaDecoderOwner* aOwner) - : MediaDecoder(aOwner) -{ - MOZ_COUNT_CTOR(DirectShowDecoder); -} - -DirectShowDecoder::~DirectShowDecoder() -{ - MOZ_COUNT_DTOR(DirectShowDecoder); -} - -} // namespace mozilla diff --git a/dom/media/directshow/DirectShowDecoder.h b/dom/media/directshow/DirectShowDecoder.h deleted file mode 100644 index 90b72aef1e..0000000000 --- a/dom/media/directshow/DirectShowDecoder.h +++ /dev/null @@ -1,45 +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/. */ - -#if !defined(DirectShowDecoder_h_) -#define DirectShowDecoder_h_ - -#include "MediaDecoder.h" - -namespace mozilla { - -// Decoder that uses the legacy DirectShow graph on systems without WMF. -class DirectShowDecoder : public MediaDecoder -{ -public: - - explicit DirectShowDecoder(MediaDecoderOwner* aOwner); - virtual ~DirectShowDecoder(); - - MediaDecoder* Clone(MediaDecoderOwner* aOwner) override { - if (!IsEnabled()) { - return nullptr; - } - return new DirectShowDecoder(aOwner); - } - - MediaDecoderStateMachine* CreateStateMachine() override; - - // Returns true if aType is a MIME type that we render with the - // DirectShow backend. If aCodecList is non null, - // it is filled with a (static const) null-terminated list of strings - // denoting the codecs we'll playback. Note that playback is strictly - // limited to MP3 only. - static bool GetSupportedCodecs(const nsACString& aType, - char const *const ** aCodecList); - - // Returns true if the DirectShow backend is preffed on. - static bool IsEnabled(); -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/directshow/DirectShowReader.cpp b/dom/media/directshow/DirectShowReader.cpp deleted file mode 100644 index cc7278146e..0000000000 --- a/dom/media/directshow/DirectShowReader.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "DirectShowReader.h" -#include "MediaDecoderReader.h" -#include "mozilla/RefPtr.h" -#include "DirectShowUtils.h" -#include "AudioSinkFilter.h" -#include "SourceFilter.h" -#include "SampleSink.h" -#include "VideoUtils.h" -#include "mozilla/Preferences.h" - -using namespace mozilla::media; - -namespace mozilla { - -// Windows XP's MP3 decoder filter. This is available on XP only, on Vista -// and later we can use the DMO Wrapper filter and MP3 decoder DMO. -const GUID DirectShowReader::CLSID_MPEG_LAYER_3_DECODER_FILTER = -{ 0x38BE3000, 0xDBF4, 0x11D0, {0x86, 0x0E, 0x00, 0xA0, 0x24, 0xCF, 0xEF, 0x6D} }; - - -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder) - : MediaDecoderReader(aDecoder), -#ifdef DIRECTSHOW_REGISTER_GRAPH - mRotRegister(0), -#endif - mNumChannels(0), - mAudioRate(0), - mBytesPerSample(0), - mIsH264(aDecoder->GetResource()->GetContentType().EqualsLiteral("video/h264") || - aDecoder->GetResource()->GetContentType().EqualsLiteral("video/avc")) -{ - MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); - MOZ_COUNT_CTOR(DirectShowReader); - if (mIsH264) { - Preferences::SetBool("media.directshow.h264.active", true); - } -} - -DirectShowReader::~DirectShowReader() -{ - MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); - MOZ_COUNT_DTOR(DirectShowReader); - if (mIsH264) { - Preferences::SetBool("media.directshow.h264.active", false); - } -#ifdef DIRECTSHOW_REGISTER_GRAPH - if (mRotRegister) { - RemoveGraphFromRunningObjectTable(mRotRegister); - } -#endif -} - -nsresult -DirectShowReader::ReadMetadata(MediaInfo* aInfo, - MetadataTags** aTags) -{ - MOZ_ASSERT(OnTaskQueue()); - HRESULT hr; - nsresult rv; - - // Create the filter graph, reference it by the GraphBuilder interface, - // to make graph building more convenient. - hr = CoCreateInstance(CLSID_FilterGraph, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IGraphBuilder, - reinterpret_cast(static_cast(getter_AddRefs(mGraph)))); - NS_ENSURE_TRUE(SUCCEEDED(hr) && mGraph, NS_ERROR_FAILURE); - -#ifdef DIRECTSHOW_REGISTER_GRAPH - hr = AddGraphToRunningObjectTable(mGraph, &mRotRegister); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - #endif - - // Extract the interface pointers we'll need from the filter graph. - hr = mGraph->QueryInterface(static_cast(getter_AddRefs(mControl))); - NS_ENSURE_TRUE(SUCCEEDED(hr) && mControl, NS_ERROR_FAILURE); - - hr = mGraph->QueryInterface(static_cast(getter_AddRefs(mMediaSeeking))); - NS_ENSURE_TRUE(SUCCEEDED(hr) && mMediaSeeking, NS_ERROR_FAILURE); - - if (mIsH264) { - return ReadH264Metadata(aInfo, aTags); - } - - // Build the graph. Create the filters we need, and connect them. We - // build the entire graph ourselves to prevent other decoders installed - // on the system being created and used. - - // Our source filters, wraps the MediaResource. - mSourceFilter = new SourceFilter(MEDIATYPE_Stream, MEDIASUBTYPE_MPEG1Audio); - NS_ENSURE_TRUE(mSourceFilter, NS_ERROR_FAILURE); - - rv = mSourceFilter->Init(mDecoder->GetResource(), 0); - NS_ENSURE_SUCCESS(rv, rv); - - hr = mGraph->AddFilter(mSourceFilter, L"MozillaDirectShowSource"); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - // The MPEG demuxer. - RefPtr demuxer; - hr = CreateAndAddFilter(mGraph, - CLSID_MPEG1Splitter, - L"MPEG1Splitter", - getter_AddRefs(demuxer)); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - // Platform MP3 decoder. - RefPtr decoder; - // Firstly try to create the MP3 decoder filter that ships with WinXP - // directly. This filter doesn't normally exist on later versions of - // Windows. - hr = CreateAndAddFilter(mGraph, - CLSID_MPEG_LAYER_3_DECODER_FILTER, - L"MPEG Layer 3 Decoder", - getter_AddRefs(decoder)); - if (FAILED(hr)) { - // Failed to create MP3 decoder filter. Try to instantiate - // the MP3 decoder DMO. - hr = AddMP3DMOWrapperFilter(mGraph, getter_AddRefs(decoder)); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - } - - // Sink, captures audio samples and inserts them into our pipeline. - static const wchar_t* AudioSinkFilterName = L"MozAudioSinkFilter"; - mAudioSinkFilter = new AudioSinkFilter(AudioSinkFilterName, &hr); - NS_ENSURE_TRUE(mAudioSinkFilter && SUCCEEDED(hr), NS_ERROR_FAILURE); - hr = mGraph->AddFilter(mAudioSinkFilter, AudioSinkFilterName); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - // Join the filters. - hr = ConnectFilters(mGraph, mSourceFilter, demuxer); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - hr = ConnectFilters(mGraph, demuxer, decoder); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - hr = ConnectFilters(mGraph, decoder, mAudioSinkFilter); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - WAVEFORMATEX format; - mAudioSinkFilter->GetSampleSink()->GetAudioFormat(&format); - NS_ENSURE_TRUE(format.wFormatTag == WAVE_FORMAT_PCM, NS_ERROR_FAILURE); - - mInfo.mAudio.mChannels = mNumChannels = format.nChannels; - mInfo.mAudio.mRate = mAudioRate = format.nSamplesPerSec; - mInfo.mAudio.mBitDepth = format.wBitsPerSample; - mBytesPerSample = format.wBitsPerSample / 8; - - // Begin decoding! - hr = mControl->Run(); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - DWORD seekCaps = 0; - hr = mMediaSeeking->GetCapabilities(&seekCaps); - mInfo.mMediaSeekable = SUCCEEDED(hr) && (AM_SEEKING_CanSeekAbsolute & seekCaps); - - LOG("Successfully initialized DirectShow MP3 decoder."); - LOG("Channels=%u Hz=%u duration=%lld bytesPerSample=%d", - mInfo.mAudio.mChannels, - mInfo.mAudio.mRate, - 0LL, - mBytesPerSample); - - *aInfo = mInfo; - // Note: The SourceFilter strips ID3v2 tags out of the stream. - *aTags = nullptr; - - return NS_OK; -} - -nsresult -DirectShowReader::ReadH264Metadata(MediaInfo* aInfo, MetadataTags** aTags) -{ - HRESULT hr; - mSourceFilter = new SourceFilter(MEDIATYPE_Stream, MEDIASUBTYPE_H264); - NS_ENSURE_TRUE(mSourceFilter, NS_ERROR_OUT_OF_MEMORY); - nsresult rv = mSourceFilter->Init(mDecoder->GetResource(), 0); - NS_ENSURE_SUCCESS(rv, rv); - hr = mGraph->AddFilter(mSourceFilter, L"Mozilla H.264 source"); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - RefPtr decoder; - hr = AddH264DecoderFilter(mGraph, getter_AddRefs(decoder)); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - mAudioSinkFilter = new AudioSinkFilter(L"Mozilla H.264 video sink", &hr); - NS_ENSURE_TRUE(mAudioSinkFilter && SUCCEEDED(hr), NS_ERROR_FAILURE); - hr = mGraph->AddFilter(mAudioSinkFilter, L"Mozilla H.264 video sink"); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - hr = ConnectFilters(mGraph, mSourceFilter, decoder); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - hr = ConnectFilters(mGraph, decoder, mAudioSinkFilter); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - const VIDEOINFOHEADER* vih = mAudioSinkFilter->GetVideoInfo(); - int32_t width = abs(vih->bmiHeader.biWidth); - int32_t height = abs(vih->bmiHeader.biHeight); - NS_ENSURE_TRUE(width > 0 && height > 0, NS_ERROR_FAILURE); - mInfo.mVideo = VideoInfo(width, height); - mInfo.mVideo.mMimeType = "video/h264"; - hr = mControl->Run(); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - *aInfo = mInfo; - *aTags = nullptr; - return NS_OK; -} - -inline float -UnsignedByteToAudioSample(uint8_t aValue) -{ - return aValue * (2.0f / UINT8_MAX) - 1.0f; -} - -bool -DirectShowReader::Finish(HRESULT aStatus) -{ - MOZ_ASSERT(OnTaskQueue()); - - LOG("DirectShowReader::Finish(0x%x)", aStatus); - // Notify the filter graph of end of stream. - RefPtr eventSink; - HRESULT hr = mGraph->QueryInterface(static_cast(getter_AddRefs(eventSink))); - if (SUCCEEDED(hr) && eventSink) { - eventSink->Notify(EC_COMPLETE, aStatus, 0); - } - return false; -} - -class DirectShowCopy -{ -public: - DirectShowCopy(uint8_t *aSource, uint32_t aBytesPerSample, - uint32_t aSamples, uint32_t aChannels) - : mSource(aSource) - , mBytesPerSample(aBytesPerSample) - , mSamples(aSamples) - , mChannels(aChannels) - , mNextSample(0) - { } - - uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples) - { - uint32_t maxSamples = std::min(aSamples, mSamples - mNextSample); - uint32_t frames = maxSamples / mChannels; - size_t byteOffset = mNextSample * mBytesPerSample; - if (mBytesPerSample == 1) { - for (uint32_t i = 0; i < maxSamples; ++i) { - uint8_t *sample = mSource + byteOffset; - aBuffer[i] = UnsignedByteToAudioSample(*sample); - byteOffset += mBytesPerSample; - } - } else if (mBytesPerSample == 2) { - for (uint32_t i = 0; i < maxSamples; ++i) { - int16_t *sample = reinterpret_cast(mSource + byteOffset); - aBuffer[i] = AudioSampleToFloat(*sample); - byteOffset += mBytesPerSample; - } - } - mNextSample += maxSamples; - return frames; - } - -private: - uint8_t * const mSource; - const uint32_t mBytesPerSample; - const uint32_t mSamples; - const uint32_t mChannels; - uint32_t mNextSample; -}; - -bool -DirectShowReader::DecodeAudioData() -{ - MOZ_ASSERT(OnTaskQueue()); - HRESULT hr; - - SampleSink* sink = mAudioSinkFilter->GetSampleSink(); - if (sink->AtEOS()) { - // End of stream. - return Finish(S_OK); - } - - // Get the next chunk of audio samples. This blocks until the sample - // arrives, or an error occurs (like the stream is shutdown). - RefPtr sample; - hr = sink->Extract(sample); - if (FAILED(hr) || hr == S_FALSE) { - return Finish(hr); - } - - int64_t start = 0, end = 0; - sample->GetMediaTime(&start, &end); - LOG("DirectShowReader::DecodeAudioData [%4.2lf-%4.2lf]", - RefTimeToSeconds(start), - RefTimeToSeconds(end)); - - LONG length = sample->GetActualDataLength(); - LONG numSamples = length / mBytesPerSample; - LONG numFrames = length / mBytesPerSample / mNumChannels; - - BYTE* data = nullptr; - hr = sample->GetPointer(&data); - NS_ENSURE_TRUE(SUCCEEDED(hr), Finish(hr)); - - mAudioCompactor.Push(mDecoder->GetResource()->Tell(), - RefTimeToUsecs(start), - mInfo.mAudio.mRate, - numFrames, - mNumChannels, - DirectShowCopy(reinterpret_cast(data), - mBytesPerSample, - numSamples, - mNumChannels)); - return true; -} - -bool -DirectShowReader::DecodeVideoFrame(bool &aKeyframeSkip, - int64_t aTimeThreshold) -{ - MOZ_ASSERT(OnTaskQueue()); - if (!mIsH264) { - return false; - } - RefPtr sample; - HRESULT hr = mAudioSinkFilter->GetSampleSink()->Extract(sample); - if (FAILED(hr) || hr == S_FALSE) { - return Finish(hr); - } - REFERENCE_TIME start = 0, end = 0; - sample->GetMediaTime(&start, &end); - BYTE* data = nullptr; - NS_ENSURE_TRUE(SUCCEEDED(sample->GetPointer(&data)), Finish(E_FAIL)); - LONG length = sample->GetActualDataLength(); - int32_t width = mInfo.mVideo.mImage.width; - int32_t height = mInfo.mVideo.mImage.height; - int32_t stride = width * 2; - NS_ENSURE_TRUE(length >= stride * height, Finish(E_FAIL)); - nsTArray y; - nsTArray u; - nsTArray v; - y.SetLength(width * height); - u.SetLength((width / 2) * (height / 2)); - v.SetLength((width / 2) * (height / 2)); - for (int32_t row = 0; row < height; ++row) { - const uint8_t* src = data + row * stride; - for (int32_t x = 0; x < width; x += 2) { - int32_t p = row * width + x; - y[p] = src[0]; y[p + 1] = src[2]; - if (!(row & 1)) { - u[(row / 2) * (width / 2) + x / 2] = src[1]; - v[(row / 2) * (width / 2) + x / 2] = src[3]; - } - src += 4; - } - } - VideoData::YCbCrBuffer buffer; - buffer.mPlanes[0] = { y.Elements(), uint32_t(width), uint32_t(height), uint32_t(width), 0, 1 }; - buffer.mPlanes[1] = { u.Elements(), uint32_t(width / 2), uint32_t(height / 2), uint32_t(width / 2), 0, 1 }; - buffer.mPlanes[2] = { v.Elements(), uint32_t(width / 2), uint32_t(height / 2), uint32_t(width / 2), 0, 1 }; - RefPtr video = VideoData::CreateAndCopyData( - mInfo.mVideo, mDecoder->GetImageContainer(), 0, RefTimeToUsecs(start), - RefTimeToUsecs(end - start), buffer, true, -1, - gfx::IntRect(0, 0, width, height)); - NS_ENSURE_TRUE(video, Finish(E_OUTOFMEMORY)); - VideoQueue().Push(video); - return true; -} - -RefPtr -DirectShowReader::Seek(SeekTarget aTarget, int64_t aEndTime) -{ - nsresult res = SeekInternal(aTarget.GetTime().ToMicroseconds()); - if (NS_FAILED(res)) { - return SeekPromise::CreateAndReject(res, __func__); - } else { - return SeekPromise::CreateAndResolve(aTarget.GetTime(), __func__); - } -} - -nsresult -DirectShowReader::SeekInternal(int64_t aTargetUs) -{ - HRESULT hr; - MOZ_ASSERT(OnTaskQueue()); - - LOG("DirectShowReader::Seek() target=%lld", aTargetUs); - - hr = mControl->Pause(); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - nsresult rv = ResetDecode(); - NS_ENSURE_SUCCESS(rv, rv); - - LONGLONG seekPosition = UsecsToRefTime(aTargetUs); - hr = mMediaSeeking->SetPositions(&seekPosition, - AM_SEEKING_AbsolutePositioning, - nullptr, - AM_SEEKING_NoPositioning); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - hr = mControl->Run(); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - return NS_OK; -} - -} // namespace mozilla diff --git a/dom/media/directshow/DirectShowReader.h b/dom/media/directshow/DirectShowReader.h deleted file mode 100644 index 32972517a2..0000000000 --- a/dom/media/directshow/DirectShowReader.h +++ /dev/null @@ -1,106 +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/. */ - -#if !defined(DirectShowReader_h_) -#define DirectShowReader_h_ - -#include "windows.h" // HRESULT, DWORD -#include "MediaDecoderReader.h" -#include "MediaResource.h" -#include "mozilla/RefPtr.h" - -// Add the graph to the Running Object Table so that we can connect -// to this graph with GraphEdit/GraphStudio. Note: you must -// also regsvr32 proppage.dll from the Windows SDK. -// See: http://msdn.microsoft.com/en-us/library/ms787252(VS.85).aspx -// #define DIRECTSHOW_REGISTER_GRAPH - -struct IGraphBuilder; -struct IMediaControl; -struct IMediaSeeking; - -namespace mozilla { - -class AudioSinkFilter; -class SourceFilter; - -// Decoder backend for decoding legacy media using DirectShow. DirectShow operates as -// a filter graph. The basic design of the DirectShowReader is that we have -// a SourceFilter that wraps the MediaResource that connects to the -// MP3 decoder filter. The MP3 decoder filter "pulls" data as it requires it -// downstream on its own thread. When the MP3 decoder has produced a block of -// decoded samples, its thread calls downstream into our AudioSinkFilter, -// passing the decoded buffer in. The AudioSinkFilter inserts the samples into -// a SampleSink object. The SampleSink blocks the MP3 decoder's thread until -// the decode thread calls DecodeAudioData(), whereupon the SampleSink -// releases the decoded samples to the decode thread, and unblocks the MP3 -// decoder's thread. The MP3 decoder can then request more data from the -// SourceFilter, and decode more data. If the decode thread calls -// DecodeAudioData() and there's no decoded samples waiting to be extracted -// in the SampleSink, the SampleSink blocks the decode thread until the MP3 -// decoder produces a decoded sample. -class DirectShowReader : public MediaDecoderReader -{ -public: - DirectShowReader(AbstractMediaDecoder* aDecoder); - - virtual ~DirectShowReader(); - - bool DecodeAudioData() override; - bool DecodeVideoFrame(bool &aKeyframeSkip, - int64_t aTimeThreshold) override; - - nsresult ReadMetadata(MediaInfo* aInfo, - MetadataTags** aTags) override; - - RefPtr - Seek(SeekTarget aTarget, int64_t aEndTime) override; - - static const GUID CLSID_MPEG_LAYER_3_DECODER_FILTER; - -private: - // Notifies the filter graph that playback is complete. aStatus is - // the code to send to the filter graph. Always returns false, so - // that we can just "return Finish()" from DecodeAudioData(). - bool Finish(HRESULT aStatus); - - nsresult SeekInternal(int64_t aTime); - nsresult ReadH264Metadata(MediaInfo* aInfo, MetadataTags** aTags); - - // DirectShow filter graph, and associated playback and seeking - // control interfaces. - RefPtr mGraph; - RefPtr mControl; - RefPtr mMediaSeeking; - - // Wraps the MediaResource, and feeds undecoded data into the filter graph. - RefPtr mSourceFilter; - - // Sits at the end of the graph, removing decoded samples from the graph. - // The graph will block while this is blocked, i.e. it will pause decoding. - RefPtr mAudioSinkFilter; - bool mIsH264; - -#ifdef DIRECTSHOW_REGISTER_GRAPH - // Used to add/remove the filter graph to the Running Object Table. You can - // connect GraphEdit/GraphStudio to the graph to observe and/or debug its - // topology and state. - DWORD mRotRegister; -#endif - - // Number of channels in the audio stream. - uint32_t mNumChannels; - - // Samples per second in the audio stream. - uint32_t mAudioRate; - - // Number of bytes per sample. Can be either 1 or 2. - uint32_t mBytesPerSample; -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/directshow/DirectShowUtils.cpp b/dom/media/directshow/DirectShowUtils.cpp deleted file mode 100644 index e76b1b2b24..0000000000 --- a/dom/media/directshow/DirectShowUtils.cpp +++ /dev/null @@ -1,457 +0,0 @@ -/* 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/. */ - -#include "DirectShowUtils.h" -#include "DirectShowReader.h" -#include "dmodshow.h" -#include "wmcodecdsp.h" -#include "dmoreg.h" -#include "mozilla/ArrayUtils.h" -#include "mozilla/RefPtr.h" -#include "nsPrintfCString.h" -#include - -#define WARN(...) NS_WARNING(nsPrintfCString(__VA_ARGS__).get()) - -namespace mozilla { - -// Create a table which maps GUIDs to a string representation of the GUID. -// This is useful for debugging purposes, for logging the GUIDs of media types. -// This is only available when logging is enabled, i.e. not in release builds. -struct GuidToName { - const char* name; - const GUID guid; -}; - -#pragma push_macro("OUR_GUID_ENTRY") -#undef OUR_GUID_ENTRY -#define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - { #name, {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} }, - -static const GuidToName GuidToNameTable[] = { -#include -}; - -#pragma pop_macro("OUR_GUID_ENTRY") - -const char* -GetDirectShowGuidName(const GUID& aGuid) -{ - const size_t len = ArrayLength(GuidToNameTable); - for (unsigned i = 0; i < len; i++) { - if (IsEqualGUID(aGuid, GuidToNameTable[i].guid)) { - return GuidToNameTable[i].name; - } - } - return "Unknown"; -} - -void -RemoveGraphFromRunningObjectTable(DWORD aRotRegister) -{ - RefPtr runningObjectTable; - if (SUCCEEDED(GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)))) { - runningObjectTable->Revoke(aRotRegister); - } -} - -HRESULT -AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister) -{ - HRESULT hr; - - RefPtr moniker; - RefPtr runningObjectTable; - - hr = GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - const size_t STRING_LENGTH = 256; - WCHAR wsz[STRING_LENGTH]; - - StringCchPrintfW(wsz, - STRING_LENGTH, - L"FilterGraph %08x pid %08x", - (DWORD_PTR)aUnkGraph, - GetCurrentProcessId()); - - hr = CreateItemMoniker(L"!", wsz, getter_AddRefs(moniker)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - hr = runningObjectTable->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, - aUnkGraph, - moniker, - aOutRotRegister); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - return S_OK; -} - -const char* -GetGraphNotifyString(long evCode) -{ -#define CASE(x) case x: return #x - switch(evCode) { - CASE(EC_ACTIVATE); // A video window is being activated or deactivated. - CASE(EC_BANDWIDTHCHANGE); // Not supported. - CASE(EC_BUFFERING_DATA); // The graph is buffering data, or has stopped buffering data. - CASE(EC_BUILT); // Send by the Video Control when a graph has been built. Not forwarded to applications. - CASE(EC_CLOCK_CHANGED); // The reference clock has changed. - CASE(EC_CLOCK_UNSET); // The clock provider was disconnected. - CASE(EC_CODECAPI_EVENT); // Sent by an encoder to signal an encoding event. - CASE(EC_COMPLETE); // All data from a particular stream has been rendered. - CASE(EC_CONTENTPROPERTY_CHANGED); // Not supported. - CASE(EC_DEVICE_LOST); // A Plug and Play device was removed or has become available again. - CASE(EC_DISPLAY_CHANGED); // The display mode has changed. - CASE(EC_END_OF_SEGMENT); // The end of a segment has been reached. - CASE(EC_EOS_SOON); // Not supported. - CASE(EC_ERROR_STILLPLAYING); // An asynchronous command to run the graph has failed. - CASE(EC_ERRORABORT); // An operation was aborted because of an error. - CASE(EC_ERRORABORTEX); // An operation was aborted because of an error. - CASE(EC_EXTDEVICE_MODE_CHANGE); // Not supported. - CASE(EC_FILE_CLOSED); // The source file was closed because of an unexpected event. - CASE(EC_FULLSCREEN_LOST); // The video renderer is switching out of full-screen mode. - CASE(EC_GRAPH_CHANGED); // The filter graph has changed. - CASE(EC_LENGTH_CHANGED); // The length of a source has changed. - CASE(EC_LOADSTATUS); // Notifies the application of progress when opening a network file. - CASE(EC_MARKER_HIT); // Not supported. - CASE(EC_NEED_RESTART); // A filter is requesting that the graph be restarted. - CASE(EC_NEW_PIN); // Not supported. - CASE(EC_NOTIFY_WINDOW); // Notifies a filter of the video renderer's window. - CASE(EC_OLE_EVENT); // A filter is passing a text string to the application. - CASE(EC_OPENING_FILE); // The graph is opening a file, or has finished opening a file. - CASE(EC_PALETTE_CHANGED); // The video palette has changed. - CASE(EC_PAUSED); // A pause request has completed. - CASE(EC_PLEASE_REOPEN); // The source file has changed. - CASE(EC_PREPROCESS_COMPLETE); // Sent by the WM ASF Writer filter when it completes the pre-processing for multipass encoding. - CASE(EC_PROCESSING_LATENCY); // Indicates the amount of time that a component is taking to process each sample. - CASE(EC_QUALITY_CHANGE); // The graph is dropping samples, for quality control. - //CASE(EC_RENDER_FINISHED); // Not supported. - CASE(EC_REPAINT); // A video renderer requires a repaint. - CASE(EC_SAMPLE_LATENCY); // Specifies how far behind schedule a component is for processing samples. - //CASE(EC_SAMPLE_NEEDED); // Requests a new input sample from the Enhanced Video Renderer (EVR) filter. - CASE(EC_SCRUB_TIME); // Specifies the time stamp for the most recent frame step. - CASE(EC_SEGMENT_STARTED); // A new segment has started. - CASE(EC_SHUTTING_DOWN); // The filter graph is shutting down, prior to being destroyed. - CASE(EC_SNDDEV_IN_ERROR); // A device error has occurred in an audio capture filter. - CASE(EC_SNDDEV_OUT_ERROR); // A device error has occurred in an audio renderer filter. - CASE(EC_STARVATION); // A filter is not receiving enough data. - CASE(EC_STATE_CHANGE); // The filter graph has changed state. - CASE(EC_STATUS); // Contains two arbitrary status strings. - CASE(EC_STEP_COMPLETE); // A filter performing frame stepping has stepped the specified number of frames. - CASE(EC_STREAM_CONTROL_STARTED); // A stream-control start command has taken effect. - CASE(EC_STREAM_CONTROL_STOPPED); // A stream-control stop command has taken effect. - CASE(EC_STREAM_ERROR_STILLPLAYING); // An error has occurred in a stream. The stream is still playing. - CASE(EC_STREAM_ERROR_STOPPED); // A stream has stopped because of an error. - CASE(EC_TIMECODE_AVAILABLE); // Not supported. - CASE(EC_UNBUILT); // Send by the Video Control when a graph has been torn down. Not forwarded to applications. - CASE(EC_USERABORT); // The user has terminated playback. - CASE(EC_VIDEO_SIZE_CHANGED); // The native video size has changed. - CASE(EC_VIDEOFRAMEREADY); // A video frame is ready for display. - CASE(EC_VMR_RECONNECTION_FAILED); // Sent by the VMR-7 and the VMR-9 when it was unable to accept a dynamic format change request from the upstream decoder. - CASE(EC_VMR_RENDERDEVICE_SET); // Sent when the VMR has selected its rendering mechanism. - CASE(EC_VMR_SURFACE_FLIPPED); // Sent when the VMR-7's allocator presenter has called the DirectDraw Flip method on the surface being presented. - CASE(EC_WINDOW_DESTROYED); // The video renderer was destroyed or removed from the graph. - CASE(EC_WMT_EVENT); // Sent by the WM ASF Reader filter when it reads ASF files protected by digital rights management (DRM). - CASE(EC_WMT_INDEX_EVENT); // Sent when an application uses the WM ASF Writer to index Windows Media Video files. - CASE(S_OK); // Success. - CASE(VFW_S_AUDIO_NOT_RENDERED); // Partial success; the audio was not rendered. - CASE(VFW_S_DUPLICATE_NAME); // Success; the Filter Graph Manager modified a filter name to avoid duplication. - CASE(VFW_S_PARTIAL_RENDER); // Partial success; some of the streams in this movie are in an unsupported format. - CASE(VFW_S_VIDEO_NOT_RENDERED); // Partial success; the video was not rendered. - CASE(E_ABORT); // Operation aborted. - CASE(E_OUTOFMEMORY); // Insufficient memory. - CASE(E_POINTER); // Null pointer argument. - CASE(VFW_E_CANNOT_CONNECT); // No combination of intermediate filters could be found to make the connection. - CASE(VFW_E_CANNOT_RENDER); // No combination of filters could be found to render the stream. - CASE(VFW_E_NO_ACCEPTABLE_TYPES); // There is no common media type between these pins. - CASE(VFW_E_NOT_IN_GRAPH); - - default: - return "Unknown Code"; - }; -#undef CASE -} - -HRESULT -CreateAndAddFilter(IGraphBuilder* aGraph, - REFGUID aFilterClsId, - LPCWSTR aFilterName, - IBaseFilter **aOutFilter) -{ - NS_ENSURE_TRUE(aGraph, E_POINTER); - NS_ENSURE_TRUE(aOutFilter, E_POINTER); - HRESULT hr; - - RefPtr filter; - hr = CoCreateInstance(aFilterClsId, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IBaseFilter, - getter_AddRefs(filter)); - if (FAILED(hr)) { - // Object probably not available on this system. - WARN("CoCreateInstance failed, hr=%x", hr); - return hr; - } - - hr = aGraph->AddFilter(filter, aFilterName); - if (FAILED(hr)) { - WARN("AddFilter failed, hr=%x", hr); - return hr; - } - - filter.forget(aOutFilter); - - return S_OK; -} - -HRESULT -CreateMP3DMOWrapperFilter(IBaseFilter **aOutFilter) -{ - NS_ENSURE_TRUE(aOutFilter, E_POINTER); - HRESULT hr; - - // Create the wrapper filter. - RefPtr filter; - hr = CoCreateInstance(CLSID_DMOWrapperFilter, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IBaseFilter, - getter_AddRefs(filter)); - if (FAILED(hr)) { - WARN("CoCreateInstance failed, hr=%x", hr); - return hr; - } - - // Query for IDMOWrapperFilter. - RefPtr dmoWrapper; - hr = filter->QueryInterface(IID_IDMOWrapperFilter, - getter_AddRefs(dmoWrapper)); - if (FAILED(hr)) { - WARN("QueryInterface failed, hr=%x", hr); - return hr; - } - - hr = dmoWrapper->Init(CLSID_CMP3DecMediaObject, DMOCATEGORY_AUDIO_DECODER); - if (FAILED(hr)) { - // Can't instantiate MP3 DMO. It doesn't exist on Windows XP, we're - // probably hitting that. Don't log warning to console, this is an - // expected error. - WARN("dmoWrapper Init failed, hr=%x", hr); - return hr; - } - - filter.forget(aOutFilter); - - return S_OK; -} - -HRESULT -AddMP3DMOWrapperFilter(IGraphBuilder* aGraph, - IBaseFilter **aOutFilter) -{ - NS_ENSURE_TRUE(aGraph, E_POINTER); - NS_ENSURE_TRUE(aOutFilter, E_POINTER); - HRESULT hr; - - // Create the wrapper filter. - RefPtr filter; - hr = CreateMP3DMOWrapperFilter(getter_AddRefs(filter)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - // Add the wrapper filter to graph. - hr = aGraph->AddFilter(filter, L"MP3 Decoder DMO"); - if (FAILED(hr)) { - WARN("AddFilter failed, hr=%x", hr); - return hr; - } - - filter.forget(aOutFilter); - - return S_OK; -} - -bool -CanDecodeMP3UsingDirectShow() -{ - RefPtr filter; - - // Can we create the MP3 demuxer filter? - if (FAILED(CoCreateInstance(CLSID_MPEG1Splitter, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IBaseFilter, - getter_AddRefs(filter)))) { - return false; - } - - // Can we create either the WinXP MP3 decoder filter or the MP3 DMO decoder? - if (FAILED(CoCreateInstance(DirectShowReader::CLSID_MPEG_LAYER_3_DECODER_FILTER, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IBaseFilter, - getter_AddRefs(filter))) && - FAILED(CreateMP3DMOWrapperFilter(getter_AddRefs(filter)))) { - return false; - } - - // Else, we can create all of the components we need. Assume - // DirectShow is going to work... - return true; -} - -bool -CanDecodeH264UsingDirectShow() -{ - RefPtr devEnum; - HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, - CLSCTX_INPROC_SERVER, - IID_ICreateDevEnum, getter_AddRefs(devEnum)); - if (FAILED(hr)) { - return false; - } - - RefPtr filters; - hr = devEnum->CreateClassEnumerator(CLSID_LegacyAmFilterCategory, - getter_AddRefs(filters), 0); - if (hr != S_OK) { - return false; - } - - RefPtr moniker; - ULONG fetched = 0; - while (filters->Next(1, getter_AddRefs(moniker), &fetched) == S_OK) { - RefPtr bag; - if (SUCCEEDED(moniker->BindToStorage(nullptr, nullptr, - IID_IPropertyBag, - getter_AddRefs(bag)))) { - VARIANT value; - VariantInit(&value); - if (SUCCEEDED(bag->Read(L"FriendlyName", &value, nullptr)) && - value.vt == VT_BSTR && value.bstrVal && - (wcsstr(value.bstrVal, L"H264") || - wcsstr(value.bstrVal, L"H.264") || - wcsstr(value.bstrVal, L"AVC"))) { - VariantClear(&value); - return true; - } - VariantClear(&value); - } - moniker = nullptr; - } - return false; -} - -HRESULT -AddH264DecoderFilter(IGraphBuilder* aGraph, IBaseFilter** aOutFilter) -{ - NS_ENSURE_TRUE(aGraph && aOutFilter, E_POINTER); - *aOutFilter = nullptr; - RefPtr devEnum; - HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, - CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, - getter_AddRefs(devEnum)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - RefPtr filters; - hr = devEnum->CreateClassEnumerator(CLSID_LegacyAmFilterCategory, - getter_AddRefs(filters), 0); - NS_ENSURE_TRUE(hr == S_OK, VFW_E_NOT_FOUND); - RefPtr moniker; - ULONG fetched = 0; - while (filters->Next(1, getter_AddRefs(moniker), &fetched) == S_OK) { - RefPtr bag; - VARIANT value; - VariantInit(&value); - bool h264 = false; - if (SUCCEEDED(moniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, - getter_AddRefs(bag))) && - SUCCEEDED(bag->Read(L"FriendlyName", &value, nullptr)) && - value.vt == VT_BSTR && value.bstrVal) { - h264 = wcsstr(value.bstrVal, L"H264") || - wcsstr(value.bstrVal, L"H.264") || - wcsstr(value.bstrVal, L"AVC"); - } - VariantClear(&value); - if (h264 && SUCCEEDED(moniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, - reinterpret_cast(aOutFilter)))) { - hr = aGraph->AddFilter(*aOutFilter, L"DirectShow H.264 decoder"); - if (SUCCEEDED(hr)) { - return hr; - } - (*aOutFilter)->Release(); - *aOutFilter = nullptr; - } - moniker = nullptr; - } - return VFW_E_NOT_FOUND; -} - -// Match a pin by pin direction and connection state. -HRESULT -MatchUnconnectedPin(IPin* aPin, - PIN_DIRECTION aPinDir, - bool *aOutMatches) -{ - NS_ENSURE_TRUE(aPin, E_POINTER); - NS_ENSURE_TRUE(aOutMatches, E_POINTER); - - // Ensure the pin is unconnected. - RefPtr peer; - HRESULT hr = aPin->ConnectedTo(getter_AddRefs(peer)); - if (hr != VFW_E_NOT_CONNECTED) { - *aOutMatches = false; - return hr; - } - - // Ensure the pin is of the specified direction. - PIN_DIRECTION pinDir; - hr = aPin->QueryDirection(&pinDir); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - *aOutMatches = (pinDir == aPinDir); - return S_OK; -} - -// Return the first unconnected input pin or output pin. -already_AddRefed -GetUnconnectedPin(IBaseFilter* aFilter, PIN_DIRECTION aPinDir) -{ - RefPtr enumPins; - - HRESULT hr = aFilter->EnumPins(getter_AddRefs(enumPins)); - NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); - - // Test each pin to see if it matches the direction we're looking for. - RefPtr pin; - while (S_OK == enumPins->Next(1, getter_AddRefs(pin), nullptr)) { - bool matches = FALSE; - if (SUCCEEDED(MatchUnconnectedPin(pin, aPinDir, &matches)) && - matches) { - return pin.forget(); - } - } - - return nullptr; -} - -HRESULT -ConnectFilters(IGraphBuilder* aGraph, - IBaseFilter* aOutputFilter, - IBaseFilter* aInputFilter) -{ - RefPtr output = GetUnconnectedPin(aOutputFilter, PINDIR_OUTPUT); - NS_ENSURE_TRUE(output, E_FAIL); - - RefPtr input = GetUnconnectedPin(aInputFilter, PINDIR_INPUT); - NS_ENSURE_TRUE(output, E_FAIL); - - return aGraph->Connect(output, input); -} - -} // namespace mozilla - -// avoid redefined macro in unified build -#undef WARN diff --git a/dom/media/directshow/DirectShowUtils.h b/dom/media/directshow/DirectShowUtils.h deleted file mode 100644 index 8043c8b30e..0000000000 --- a/dom/media/directshow/DirectShowUtils.h +++ /dev/null @@ -1,134 +0,0 @@ -/* 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/. */ - -#ifndef _DirectShowUtils_h_ -#define _DirectShowUtils_h_ - -#include -#include "dshow.h" - -// XXXbz windowsx.h defines GetFirstChild, GetNextSibling, -// GetPrevSibling are macros, apparently... Eeevil. We have functions -// called that on some classes, so undef them. -#undef GetFirstChild -#undef GetNextSibling -#undef GetPrevSibling - -#include "DShowTools.h" -#include "mozilla/Logging.h" - -namespace mozilla { - -// Win32 "Event" wrapper. Must be paired with a CriticalSection to create a -// Java-style "monitor". -class Signal { -public: - - Signal(CriticalSection* aLock) - : mLock(aLock) - { - CriticalSectionAutoEnter lock(*mLock); - mEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - } - - ~Signal() { - CriticalSectionAutoEnter lock(*mLock); - CloseHandle(mEvent); - } - - // Lock must be held. - void Notify() { - SetEvent(mEvent); - } - - // Lock must be held. Check the wait condition before waiting! - HRESULT Wait() { - mLock->Leave(); - DWORD result = WaitForSingleObject(mEvent, INFINITE); - mLock->Enter(); - return result == WAIT_OBJECT_0 ? S_OK : E_FAIL; - } - -private: - CriticalSection* mLock; - HANDLE mEvent; -}; - -HRESULT -AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister); - -void -RemoveGraphFromRunningObjectTable(DWORD aRotRegister); - -const char* -GetGraphNotifyString(long evCode); - -// Creates a filter and adds it to a graph. -HRESULT -CreateAndAddFilter(IGraphBuilder* aGraph, - REFGUID aFilterClsId, - LPCWSTR aFilterName, - IBaseFilter **aOutFilter); - -HRESULT -AddMP3DMOWrapperFilter(IGraphBuilder* aGraph, - IBaseFilter **aOutFilter); - -// Connects the output pin on aOutputFilter to an input pin on -// aInputFilter, in aGraph. -HRESULT -ConnectFilters(IGraphBuilder* aGraph, - IBaseFilter* aOutputFilter, - IBaseFilter* aInputFilter); - -HRESULT -MatchUnconnectedPin(IPin* aPin, - PIN_DIRECTION aPinDir, - bool *aOutMatches); - -// Converts from microseconds to DirectShow "Reference Time" -// (hundreds of nanoseconds). -inline int64_t -UsecsToRefTime(const int64_t aUsecs) -{ - return aUsecs * 10; -} - -// Converts from DirectShow "Reference Time" (hundreds of nanoseconds) -// to microseconds. -inline int64_t -RefTimeToUsecs(const int64_t hRefTime) -{ - return hRefTime / 10; -} - -// Converts from DirectShow "Reference Time" (hundreds of nanoseconds) -// to seconds. -inline double -RefTimeToSeconds(const REFERENCE_TIME aRefTime) -{ - return double(aRefTime) / 10000000; -} - -const char* -GetDirectShowGuidName(const GUID& aGuid); - -// Returns true if we can instantiate an MP3 demuxer and decoder filters. -// Use this to detect whether MP3 support is installed. -bool -CanDecodeMP3UsingDirectShow(); - -// Returns true when a registered DirectShow filter advertises H.264/AVC -// decoding. This works on Windows 2000 because it uses the legacy filter -// enumerator instead of Media Foundation. -bool -CanDecodeH264UsingDirectShow(); - -HRESULT -AddH264DecoderFilter(IGraphBuilder* aGraph, IBaseFilter** aOutFilter); - -} // namespace mozilla - -#endif diff --git a/dom/media/directshow/SampleSink.cpp b/dom/media/directshow/SampleSink.cpp deleted file mode 100644 index fa5dc8d19c..0000000000 --- a/dom/media/directshow/SampleSink.cpp +++ /dev/null @@ -1,159 +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/. */ - -#include "SampleSink.h" -#include "AudioSinkFilter.h" -#include "AudioSinkInputPin.h" -#include "VideoUtils.h" -#include "mozilla/Logging.h" - -using namespace mozilla::media; - -namespace mozilla { - -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -SampleSink::SampleSink() - : mMonitor("SampleSink"), - mIsFlushing(false), - mAtEOS(false) -{ - MOZ_COUNT_CTOR(SampleSink); -} - -SampleSink::~SampleSink() -{ - MOZ_COUNT_DTOR(SampleSink); -} - -void -SampleSink::SetAudioFormat(const WAVEFORMATEX* aInFormat) -{ - NS_ENSURE_TRUE(aInFormat, ); - ReentrantMonitorAutoEnter mon(mMonitor); - memcpy(&mAudioFormat, aInFormat, sizeof(WAVEFORMATEX)); -} - -void -SampleSink::GetAudioFormat(WAVEFORMATEX* aOutFormat) -{ - MOZ_ASSERT(aOutFormat); - ReentrantMonitorAutoEnter mon(mMonitor); - memcpy(aOutFormat, &mAudioFormat, sizeof(WAVEFORMATEX)); -} - -HRESULT -SampleSink::Receive(IMediaSample* aSample) -{ - ReentrantMonitorAutoEnter mon(mMonitor); - - while (true) { - if (mIsFlushing) { - return S_FALSE; - } - if (!mSample) { - break; - } - if (mAtEOS) { - return E_UNEXPECTED; - } - // Wait until the consumer thread consumes the sample. - mon.Wait(); - } - - if (MOZ_LOG_TEST(gDirectShowLog, LogLevel::Debug)) { - REFERENCE_TIME start = 0, end = 0; - HRESULT hr = aSample->GetMediaTime(&start, &end); - LOG("SampleSink::Receive() [%4.2lf-%4.2lf]", - (double)RefTimeToUsecs(start) / USECS_PER_S, - (double)RefTimeToUsecs(end) / USECS_PER_S); - } - - mSample = aSample; - // Notify the signal, to awaken the consumer thread in WaitForSample() - // if necessary. - mon.NotifyAll(); - return S_OK; -} - -HRESULT -SampleSink::Extract(RefPtr& aOutSample) -{ - ReentrantMonitorAutoEnter mon(mMonitor); - // Loop until we have a sample, or we should abort. - while (true) { - if (mIsFlushing) { - return S_FALSE; - } - if (mSample) { - break; - } - if (mAtEOS) { - // Order is important here, if we have a sample, we should return it - // before reporting EOS. - return E_UNEXPECTED; - } - // Wait until the producer thread gives us a sample. - mon.Wait(); - } - aOutSample = mSample; - - if (MOZ_LOG_TEST(gDirectShowLog, LogLevel::Debug)) { - int64_t start = 0, end = 0; - mSample->GetMediaTime(&start, &end); - LOG("SampleSink::Extract() [%4.2lf-%4.2lf]", - (double)RefTimeToUsecs(start) / USECS_PER_S, - (double)RefTimeToUsecs(end) / USECS_PER_S); - } - - mSample = nullptr; - // Notify the signal, to awaken the producer thread in Receive() - // if necessary. - mon.NotifyAll(); - return S_OK; -} - -void -SampleSink::Flush() -{ - LOG("SampleSink::Flush()"); - ReentrantMonitorAutoEnter mon(mMonitor); - mIsFlushing = true; - mSample = nullptr; - mon.NotifyAll(); -} - -void -SampleSink::Reset() -{ - LOG("SampleSink::Reset()"); - ReentrantMonitorAutoEnter mon(mMonitor); - mIsFlushing = false; - mAtEOS = false; -} - -void -SampleSink::SetEOS() -{ - LOG("SampleSink::SetEOS()"); - ReentrantMonitorAutoEnter mon(mMonitor); - mAtEOS = true; - // Notify to unblock any threads waiting for samples in - // Extract() or Receive(). Now that we're at EOS, no more samples - // will come! - mon.NotifyAll(); -} - -bool -SampleSink::AtEOS() -{ - ReentrantMonitorAutoEnter mon(mMonitor); - return mAtEOS && !mSample; -} - -} // namespace mozilla - diff --git a/dom/media/directshow/SampleSink.h b/dom/media/directshow/SampleSink.h deleted file mode 100644 index 6a1af9fee4..0000000000 --- a/dom/media/directshow/SampleSink.h +++ /dev/null @@ -1,67 +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/. */ - -#if !defined(SampleSink_h_) -#define SampleSink_h_ - -#include "BaseFilter.h" -#include "DirectShowUtils.h" -#include "mozilla/RefPtr.h" -#include "mozilla/ReentrantMonitor.h" - -namespace mozilla { - -class SampleSink { -public: - SampleSink(); - virtual ~SampleSink(); - - // Sets the audio format of the incoming samples. The upstream filter - // calls this. This makes a copy. - void SetAudioFormat(const WAVEFORMATEX* aInFormat); - - // Copies the format of incoming audio samples into into *aOutFormat. - void GetAudioFormat(WAVEFORMATEX* aOutFormat); - - // Called when a sample is delivered by the DirectShow graph to the sink. - // The decode thread retrieves the sample by calling WaitForSample(). - // Blocks if there's already a sample waiting to be consumed by the decode - // thread. - HRESULT Receive(IMediaSample* aSample); - - // Retrieves a sample from the sample queue, blocking until one becomes - // available, or until an error occurs. Returns S_FALSE on EOS. - HRESULT Extract(RefPtr& aOutSample); - - // Unblocks any threads waiting in GetSample(). - // Clears mSample, which unblocks upstream stream. - void Flush(); - - // Opens up the sink to receive more samples in PutSample(). - // Clears EOS flag. - void Reset(); - - // Marks that we've reacehd the end of stream. - void SetEOS(); - - // Returns whether we're at end of stream. - bool AtEOS(); - -private: - // All data in this class is syncronized by mMonitor. - ReentrantMonitor mMonitor; - RefPtr mSample; - - // Format of the audio stream we're receiving. - WAVEFORMATEX mAudioFormat; - - bool mIsFlushing; - bool mAtEOS; -}; - -} // namespace mozilla - -#endif // SampleSink_h_ diff --git a/dom/media/directshow/SourceFilter.cpp b/dom/media/directshow/SourceFilter.cpp deleted file mode 100644 index f5ef11ec78..0000000000 --- a/dom/media/directshow/SourceFilter.cpp +++ /dev/null @@ -1,682 +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/. */ - -#include "SourceFilter.h" -#include "MediaResource.h" -#include "mozilla/RefPtr.h" -#include "DirectShowUtils.h" -#include "mozilla/Logging.h" -#include - -using namespace mozilla::media; - -namespace mozilla { - -// Define to trace what's on... -//#define DEBUG_SOURCE_TRACE 1 - -#if defined (DEBUG_SOURCE_TRACE) -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define DIRECTSHOW_LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) -#else -#define DIRECTSHOW_LOG(...) -#endif - -static HRESULT -DoGetInterface(IUnknown* aUnknown, void** aInterface) -{ - if (!aInterface) - return E_POINTER; - *aInterface = aUnknown; - aUnknown->AddRef(); - return S_OK; -} - -// Stores details of IAsyncReader::Request(). -class ReadRequest { -public: - - ReadRequest(IMediaSample* aSample, - DWORD_PTR aDwUser, - uint32_t aOffset, - uint32_t aCount) - : mSample(aSample), - mDwUser(aDwUser), - mOffset(aOffset), - mCount(aCount) - { - MOZ_COUNT_CTOR(ReadRequest); - } - - ~ReadRequest() { - MOZ_COUNT_DTOR(ReadRequest); - } - - RefPtr mSample; - DWORD_PTR mDwUser; - uint32_t mOffset; - uint32_t mCount; -}; - -// A wrapper around media resource that presents only a partition of the -// underlying resource to the caller to use. The partition returned is from -// an offset to the end of stream, and this object deals with ensuring -// the offsets and lengths etc are translated from the reduced partition -// exposed to the caller, to the absolute offsets of the underlying stream. -class MediaResourcePartition { -public: - MediaResourcePartition(MediaResource* aResource, - int64_t aDataStart) - : mResource(aResource), - mDataOffset(aDataStart) - {} - - int64_t GetLength() { - int64_t len = mResource.GetLength(); - if (len == -1) { - return len; - } - return std::max(0, len - mDataOffset); - } - nsresult ReadAt(int64_t aOffset, char* aBuffer, - uint32_t aCount, uint32_t* aBytes) - { - return mResource.ReadAt(aOffset + mDataOffset, - aBuffer, - aCount, - aBytes); - } - int64_t GetCachedDataEnd() { - int64_t tell = mResource.GetResource()->Tell(); - int64_t dataEnd = - mResource.GetResource()->GetCachedDataEnd(tell) - mDataOffset; - return dataEnd; - } -private: - // MediaResource from which we read data. - MediaResourceIndex mResource; - int64_t mDataOffset; -}; - - -// Output pin for SourceFilter, which implements IAsyncReader, to -// allow downstream filters to pull/read data from it. Downstream pins -// register to read data using Request(), and asynchronously wait for the -// reads to complete using WaitForNext(). They may also synchronously read -// using SyncRead(). This class is a delegate (tear off) of -// SourceFilter. -// -// We can expose only a segment of the MediaResource to the filter graph. -// This is used to strip off the ID3v2 tags from the stream, as DirectShow -// has trouble parsing some headers. -// -// Implements: -// * IAsyncReader -// * IPin -// * IQualityControl -// * IUnknown -// -class DECLSPEC_UUID("18e5cfb2-1015-440c-a65c-e63853235894") -OutputPin : public IAsyncReader, - public BasePin -{ -public: - - OutputPin(MediaResource* aMediaResource, - SourceFilter* aParent, - CriticalSection& aFilterLock, - int64_t aMP3DataStart); - virtual ~OutputPin(); - - // IUnknown - // Defer to ref counting to BasePin, which defers to owning nsBaseFilter. - STDMETHODIMP_(ULONG) AddRef() override { return BasePin::AddRef(); } - STDMETHODIMP_(ULONG) Release() override { return BasePin::Release(); } - STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override; - - // BasePin Overrides. - // Determines if the pin accepts a specific media type. - HRESULT CheckMediaType(const MediaType* aMediaType) override; - - // Retrieves a preferred media type, by index value. - HRESULT GetMediaType(int aPosition, MediaType* aMediaType) override; - - // Releases the pin from a connection. - HRESULT BreakConnect(void) override; - - // Determines whether a pin connection is suitable. - HRESULT CheckConnect(IPin* aPin) override; - - - // IAsyncReader overrides - - // The RequestAllocator method requests an allocator during the - // pin connection. - STDMETHODIMP RequestAllocator(IMemAllocator* aPreferred, - ALLOCATOR_PROPERTIES* aProps, - IMemAllocator** aActual) override; - - // The Request method queues an asynchronous request for data. Downstream - // will call WaitForNext() when they want to retrieve the result. - STDMETHODIMP Request(IMediaSample* aSample, DWORD_PTR aUserData) override; - - // The WaitForNext method waits for the next pending read request - // to complete. This method fails if the graph is flushing. - // Defers to SyncRead/5. - STDMETHODIMP WaitForNext(DWORD aTimeout, - IMediaSample** aSamples, - DWORD_PTR* aUserData) override; - - // The SyncReadAligned method performs a synchronous read. The method - // blocks until the request is completed. Defers to SyncRead/5. This - // method does not fail if the graph is flushing. - STDMETHODIMP SyncReadAligned(IMediaSample* aSample) override; - - // The SyncRead method performs a synchronous read. The method blocks - // until the request is completed. Defers to SyncRead/5. This - // method does not fail if the graph is flushing. - STDMETHODIMP SyncRead(LONGLONG aPosition, LONG aLength, BYTE* aBuffer) override; - - // The Length method retrieves the total length of the stream. - STDMETHODIMP Length(LONGLONG* aTotal, LONGLONG* aAvailable) override; - - // IPin Overrides - STDMETHODIMP BeginFlush(void) override; - STDMETHODIMP EndFlush(void) override; - - uint32_t GetAndResetBytesConsumedCount(); - -private: - - // Protects thread-shared data/structures (mFlushCount, mPendingReads). - // WaitForNext() also waits on this monitor - CriticalSection& mPinLock; - - // Signal used with mPinLock to implement WaitForNext(). - Signal mSignal; - - // The filter that owns us. Weak reference, as we're a delegate (tear off). - SourceFilter* mParentSource; - - MediaResourcePartition mResource; - - // Counter, inc'd in BeginFlush(), dec'd in EndFlush(). Calls to this can - // come from multiple threads and can interleave, hence the counter. - int32_t mFlushCount; - - // Number of bytes that have been read from the output pin since the last - // time GetAndResetBytesConsumedCount() was called. - uint32_t mBytesConsumed; - - // Deque of ReadRequest* for reads that are yet to be serviced. - // nsReadRequest's are stored on the heap, popper must delete them. - nsDeque mPendingReads; - - // Flags if the downstream pin has QI'd for IAsyncReader. We refuse - // connection if they don't query, as it means they're assuming that we're - // a push filter, and we're not. - bool mQueriedForAsyncReader; - -}; - -// For mingw __uuidof support -#ifdef __CRT_UUID_DECL -} -__CRT_UUID_DECL(mozilla::OutputPin, 0x18e5cfb2,0x1015,0x440c,0xa6,0x5c,0xe6,0x38,0x53,0x23,0x58,0x94); -namespace mozilla { -#endif - -OutputPin::OutputPin(MediaResource* aResource, - SourceFilter* aParent, - CriticalSection& aFilterLock, - int64_t aMP3DataStart) - : BasePin(static_cast(aParent), - &aFilterLock, - L"MozillaOutputPin", - PINDIR_OUTPUT), - mPinLock(aFilterLock), - mSignal(&mPinLock), - mParentSource(aParent), - mResource(aResource, aMP3DataStart), - mFlushCount(0), - mBytesConsumed(0), - mQueriedForAsyncReader(false) -{ - MOZ_COUNT_CTOR(OutputPin); - DIRECTSHOW_LOG("OutputPin::OutputPin()"); -} - -OutputPin::~OutputPin() -{ - MOZ_COUNT_DTOR(OutputPin); - DIRECTSHOW_LOG("OutputPin::~OutputPin()"); -} - -HRESULT -OutputPin::BreakConnect() -{ - mQueriedForAsyncReader = false; - return BasePin::BreakConnect(); -} - -STDMETHODIMP -OutputPin::QueryInterface(REFIID aIId, void** aInterface) -{ - if (aIId == IID_IAsyncReader) { - mQueriedForAsyncReader = true; - return DoGetInterface(static_cast(this), aInterface); - } - - if (aIId == __uuidof(OutputPin)) { - AddRef(); - *aInterface = this; - return S_OK; - } - - return BasePin::QueryInterface(aIId, aInterface); -} - -HRESULT -OutputPin::CheckConnect(IPin* aPin) -{ - // Our connection is only suitable if the downstream pin knows - // that we're asynchronous (i.e. it queried for IAsyncReader). - return mQueriedForAsyncReader ? S_OK : S_FALSE; -} - -HRESULT -OutputPin::CheckMediaType(const MediaType* aMediaType) -{ - const MediaType *myMediaType = mParentSource->GetMediaType(); - - if (IsEqualGUID(aMediaType->majortype, myMediaType->majortype) && - IsEqualGUID(aMediaType->subtype, myMediaType->subtype) && - IsEqualGUID(aMediaType->formattype, myMediaType->formattype)) - { - DIRECTSHOW_LOG("OutputPin::CheckMediaType() Match: major=%s minor=%s TC=%d FSS=%d SS=%u", - GetDirectShowGuidName(aMediaType->majortype), - GetDirectShowGuidName(aMediaType->subtype), - aMediaType->TemporalCompression(), - aMediaType->bFixedSizeSamples, - aMediaType->SampleSize()); - return S_OK; - } - - DIRECTSHOW_LOG("OutputPin::CheckMediaType() Failed to match: major=%s minor=%s TC=%d FSS=%d SS=%u", - GetDirectShowGuidName(aMediaType->majortype), - GetDirectShowGuidName(aMediaType->subtype), - aMediaType->TemporalCompression(), - aMediaType->bFixedSizeSamples, - aMediaType->SampleSize()); - return S_FALSE; -} - -HRESULT -OutputPin::GetMediaType(int aPosition, MediaType* aMediaType) -{ - if (!aMediaType) - return E_POINTER; - - if (aPosition == 0) { - aMediaType->Assign(mParentSource->GetMediaType()); - return S_OK; - } - return VFW_S_NO_MORE_ITEMS; -} - -static inline bool -IsPowerOf2(int32_t x) { - return ((-x & x) != x); -} - -STDMETHODIMP -OutputPin::RequestAllocator(IMemAllocator* aPreferred, - ALLOCATOR_PROPERTIES* aProps, - IMemAllocator** aActual) -{ - // Require the downstream pin to suggest what they want... - if (!aPreferred) return E_POINTER; - if (!aProps) return E_POINTER; - if (!aActual) return E_POINTER; - - // We only care about alignment - our allocator will reject anything - // which isn't power-of-2 aligned, so so try a 4-byte aligned allocator. - ALLOCATOR_PROPERTIES props; - memcpy(&props, aProps, sizeof(ALLOCATOR_PROPERTIES)); - if (aProps->cbAlign == 0 || IsPowerOf2(aProps->cbAlign)) { - props.cbAlign = 4; - } - - // Limit allocator's number of buffers. We know that the media will most - // likely be bound by network speed, not by decoding speed. We also - // store the incoming data in a Gecko stream, if we don't limit buffers - // here we'll end up duplicating a lot of storage. We must have enough - // space for audio key frames to fit in the first batch of buffers however, - // else pausing may fail for some downstream decoders. - if (props.cBuffers > BaseFilter::sMaxNumBuffers) { - props.cBuffers = BaseFilter::sMaxNumBuffers; - } - - // The allocator properties that are actually used. We don't store - // this, we need it for SetProperties() below to succeed. - ALLOCATOR_PROPERTIES actualProps; - HRESULT hr; - - if (aPreferred) { - // Play nice and prefer the downstream pin's preferred allocator. - hr = aPreferred->SetProperties(&props, &actualProps); - if (SUCCEEDED(hr)) { - aPreferred->AddRef(); - *aActual = aPreferred; - return S_OK; - } - } - - // Else downstream hasn't requested a specific allocator, so create one... - - // Just create a default allocator. It's highly unlikely that we'll use - // this anyway, as most parsers insist on using their own allocators. - RefPtr allocator; - hr = CoCreateInstance(CLSID_MemoryAllocator, - 0, - CLSCTX_INPROC_SERVER, - IID_IMemAllocator, - getter_AddRefs(allocator)); - if(FAILED(hr) || (allocator == nullptr)) { - NS_WARNING("Can't create our own DirectShow allocator."); - return hr; - } - - // See if we can make it suitable - hr = allocator->SetProperties(&props, &actualProps); - if (SUCCEEDED(hr)) { - // We need to release our refcount on pAlloc, and addref - // it to pass a refcount to the caller - this is a net nothing. - allocator.forget(aActual); - return S_OK; - } - - NS_WARNING("Failed to pick an allocator"); - return hr; -} - -STDMETHODIMP -OutputPin::Request(IMediaSample* aSample, DWORD_PTR aDwUser) -{ - if (!aSample) return E_FAIL; - - CriticalSectionAutoEnter lock(*mLock); - NS_ASSERTION(!mFlushCount, "Request() while flushing"); - - if (mFlushCount) - return VFW_E_WRONG_STATE; - - REFERENCE_TIME refStart = 0, refEnd = 0; - if (FAILED(aSample->GetTime(&refStart, &refEnd))) { - NS_WARNING("Sample incorrectly timestamped"); - return VFW_E_SAMPLE_TIME_NOT_SET; - } - - // Convert reference time to bytes. - uint32_t start = (uint32_t)(refStart / 10000000); - uint32_t end = (uint32_t)(refEnd / 10000000); - - uint32_t numBytes = end - start; - - ReadRequest* request = new ReadRequest(aSample, - aDwUser, - start, - numBytes); - // Memory for |request| is free when it's popped from the completed - // reads list. - - // Push this onto the queue of reads to be serviced. - mPendingReads.Push(request); - - // Notify any threads blocked in WaitForNext() which are waiting for mPendingReads - // to become non-empty. - mSignal.Notify(); - - return S_OK; -} - -STDMETHODIMP -OutputPin::WaitForNext(DWORD aTimeout, - IMediaSample** aOutSample, - DWORD_PTR* aOutDwUser) -{ - NS_ASSERTION(aTimeout == 0 || aTimeout == INFINITE, - "Oops, we don't handle this!"); - - *aOutSample = nullptr; - *aOutDwUser = 0; - - LONGLONG offset = 0; - LONG count = 0; - BYTE* buf = nullptr; - - { - CriticalSectionAutoEnter lock(*mLock); - - // Wait until there's a pending read to service. - while (aTimeout && mPendingReads.GetSize() == 0 && !mFlushCount) { - // Note: No need to guard against shutdown-during-wait here, as - // typically the thread doing the pull will have already called - // Request(), so we won't Wait() here anyway. SyncRead() will fail - // on shutdown. - mSignal.Wait(); - } - - nsAutoPtr request(reinterpret_cast(mPendingReads.PopFront())); - if (!request) - return VFW_E_WRONG_STATE; - - *aOutSample = request->mSample; - *aOutDwUser = request->mDwUser; - - offset = request->mOffset; - count = request->mCount; - buf = nullptr; - request->mSample->GetPointer(&buf); - NS_ASSERTION(buf != nullptr, "Invalid buffer!"); - - if (mFlushCount) { - return VFW_E_TIMEOUT; - } - } - - return SyncRead(offset, count, buf); -} - -STDMETHODIMP -OutputPin::SyncReadAligned(IMediaSample* aSample) -{ - { - // Ignore reads while flushing. - CriticalSectionAutoEnter lock(*mLock); - if (mFlushCount) { - return S_FALSE; - } - } - - if (!aSample) - return E_FAIL; - - REFERENCE_TIME lStart = 0, lEnd = 0; - if (FAILED(aSample->GetTime(&lStart, &lEnd))) { - NS_WARNING("Sample incorrectly timestamped"); - return VFW_E_SAMPLE_TIME_NOT_SET; - } - - // Convert reference time to bytes. - int32_t start = (int32_t)(lStart / 10000000); - int32_t end = (int32_t)(lEnd / 10000000); - - int32_t numBytes = end - start; - - // If the range extends off the end of stream, truncate to the end of stream - // as per IAsyncReader specificiation. - int64_t streamLength = mResource.GetLength(); - if (streamLength != -1) { - // We know the exact length of the stream, fail if the requested offset - // is beyond it. - if (start > streamLength) { - return VFW_E_BADALIGN; - } - - // If the end of the chunk to read is off the end of the stream, - // truncate it to the end of the stream. - if ((start + numBytes) > streamLength) { - numBytes = (uint32_t)(streamLength - start); - } - } - - BYTE* buf=0; - aSample->GetPointer(&buf); - - return SyncRead(start, numBytes, buf); -} - -STDMETHODIMP -OutputPin::SyncRead(LONGLONG aPosition, - LONG aLength, - BYTE* aBuffer) -{ - MOZ_ASSERT(!NS_IsMainThread()); - NS_ENSURE_TRUE(aPosition >= 0, E_FAIL); - NS_ENSURE_TRUE(aLength > 0, E_FAIL); - NS_ENSURE_TRUE(aBuffer, E_POINTER); - - DIRECTSHOW_LOG("OutputPin::SyncRead(%lld, %d)", aPosition, aLength); - { - // Ignore reads while flushing. - CriticalSectionAutoEnter lock(*mLock); - if (mFlushCount) { - return S_FALSE; - } - } - - uint32_t totalBytesRead = 0; - nsresult rv = mResource.ReadAt(aPosition, - reinterpret_cast(aBuffer), - aLength, - &totalBytesRead); - if (NS_FAILED(rv)) { - return E_FAIL; - } - if (totalBytesRead > 0) { - CriticalSectionAutoEnter lock(*mLock); - mBytesConsumed += totalBytesRead; - } - return (totalBytesRead == aLength) ? S_OK : S_FALSE; -} - -STDMETHODIMP -OutputPin::Length(LONGLONG* aTotal, LONGLONG* aAvailable) -{ - HRESULT hr = S_OK; - int64_t length = mResource.GetLength(); - if (length == -1) { - hr = VFW_S_ESTIMATED; - // Don't have a length. Just lie, it seems to work... - *aTotal = INT32_MAX; - } else { - *aTotal = length; - } - if (aAvailable) { - *aAvailable = mResource.GetCachedDataEnd(); - } - - DIRECTSHOW_LOG("OutputPin::Length() len=%lld avail=%lld", *aTotal, *aAvailable); - - return hr; -} - -STDMETHODIMP -OutputPin::BeginFlush() -{ - CriticalSectionAutoEnter lock(*mLock); - mFlushCount++; - mSignal.Notify(); - return S_OK; -} - -STDMETHODIMP -OutputPin::EndFlush(void) -{ - CriticalSectionAutoEnter lock(*mLock); - mFlushCount--; - return S_OK; -} - -uint32_t -OutputPin::GetAndResetBytesConsumedCount() -{ - CriticalSectionAutoEnter lock(*mLock); - uint32_t bytesConsumed = mBytesConsumed; - mBytesConsumed = 0; - return bytesConsumed; -} - -SourceFilter::SourceFilter(const GUID& aMajorType, - const GUID& aSubType) - : BaseFilter(L"MozillaDirectShowSource", __uuidof(SourceFilter)) -{ - MOZ_COUNT_CTOR(SourceFilter); - mMediaType.majortype = aMajorType; - mMediaType.subtype = aSubType; - - DIRECTSHOW_LOG("SourceFilter Constructor(%s, %s)", - GetDirectShowGuidName(aMajorType), - GetDirectShowGuidName(aSubType)); -} - -SourceFilter::~SourceFilter() -{ - MOZ_COUNT_DTOR(SourceFilter); - DIRECTSHOW_LOG("SourceFilter Destructor()"); -} - -BasePin* -SourceFilter::GetPin(int n) -{ - if (n == 0) { - NS_ASSERTION(mOutputPin != 0, "GetPin with no pin!"); - return static_cast(mOutputPin); - } else { - return nullptr; - } -} - -// Get's the media type we're supplying. -const MediaType* -SourceFilter::GetMediaType() const -{ - return &mMediaType; -} - -nsresult -SourceFilter::Init(MediaResource* aResource, int64_t aMP3Offset) -{ - DIRECTSHOW_LOG("SourceFilter::Init()"); - - mOutputPin = new OutputPin(aResource, - this, - mLock, - aMP3Offset); - NS_ENSURE_TRUE(mOutputPin != nullptr, NS_ERROR_FAILURE); - - return NS_OK; -} - -uint32_t -SourceFilter::GetAndResetBytesConsumedCount() -{ - return mOutputPin->GetAndResetBytesConsumedCount(); -} - - -} // namespace mozilla diff --git a/dom/media/directshow/SourceFilter.h b/dom/media/directshow/SourceFilter.h deleted file mode 100644 index d5ce2770e9..0000000000 --- a/dom/media/directshow/SourceFilter.h +++ /dev/null @@ -1,75 +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/. */ - -#if !defined(nsDirectShowSource_h___) -#define nsDirectShowSource_h___ - -#include "BaseFilter.h" -#include "BasePin.h" -#include "MediaType.h" - -#include "nsDeque.h" -#include "nsAutoPtr.h" -#include "DirectShowUtils.h" -#include "mozilla/RefPtr.h" - -namespace mozilla { - -class MediaResource; -class OutputPin; - - -// SourceFilter is an asynchronous DirectShow source filter which -// reads from an MediaResource, and supplies data via a pull model downstream -// using OutputPin. It us used to supply a generic byte stream into -// DirectShow. -// -// Implements: -// * IBaseFilter -// * IMediaFilter -// * IPersist -// * IUnknown -// -class DECLSPEC_UUID("5c2a7ad0-ba82-4659-9178-c4719a2765d6") -SourceFilter : public media::BaseFilter -{ -public: - - // Constructs source filter to deliver given media type. - SourceFilter(const GUID& aMajorType, const GUID& aSubType); - ~SourceFilter(); - - nsresult Init(MediaResource *aResource, int64_t aMP3Offset); - - // BaseFilter overrides. - // Only one output - the byte stream. - int GetPinCount() override { return 1; } - - media::BasePin* GetPin(int n) override; - - // Get's the media type we're supplying. - const media::MediaType* GetMediaType() const; - - uint32_t GetAndResetBytesConsumedCount(); - -protected: - - // Our async pull output pin. - nsAutoPtr mOutputPin; - - // Type of byte stream we output. - media::MediaType mMediaType; - -}; - -} // namespace mozilla - -// For mingw __uuidof support -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(mozilla::SourceFilter, 0x5c2a7ad0,0xba82,0x4659,0x91,0x78,0xc4,0x71,0x9a,0x27,0x65,0xd6); -#endif - -#endif diff --git a/dom/media/directshow/moz.build b/dom/media/directshow/moz.build deleted file mode 100644 index 06cae45fff..0000000000 --- a/dom/media/directshow/moz.build +++ /dev/null @@ -1,42 +0,0 @@ -# -*- Mode: python; 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/. - -EXPORTS += [ - 'AudioSinkFilter.h', - 'AudioSinkInputPin.h', - 'DirectShowDecoder.h', - 'DirectShowReader.h', - 'DirectShowUtils.h', -] - -UNIFIED_SOURCES += [ - 'DirectShowDecoder.cpp', - 'DirectShowUtils.cpp', - 'SourceFilter.cpp', -] - -SOURCES += [ - 'AudioSinkFilter.cpp', - 'AudioSinkInputPin.cpp', - 'DirectShowReader.cpp', - 'SampleSink.cpp', -] - -# If WebRTC isn't being built, we need to compile the DirectShow base classes so that -# they're available at link time. -if not CONFIG['MOZ_WEBRTC']: - SOURCES += [ - '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseFilter.cpp', - '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseInputPin.cpp', - '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BasePin.cpp', - '/media/webrtc/trunk/webrtc/modules/video_capture/windows/MediaType.cpp', - ] - -FINAL_LIBRARY = 'xul' -LOCAL_INCLUDES += [ - '/dom/media', - '/media/webrtc/trunk/webrtc/modules/video_capture/windows', -] diff --git a/dom/media/moz.build b/dom/media/moz.build index bd20936d7a..300c6ae347 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -42,12 +42,6 @@ DIRS += [ 'standalone', ] -if CONFIG['MOZ_DIRECTSHOW']: - DIRS += ['directshow'] - LOCAL_INCLUDES += [ - '/media/webrtc/trunk/webrtc/modules/video_capture/windows', - ] - if CONFIG['MOZ_FMP4']: DIRS += ['fmp4'] diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 5e124ff937..54d89a7abb 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -392,11 +392,6 @@ pref("media.wave.play-stand-alone", false); pref("media.hardware-video-decoding.enabled", true); pref("media.hardware-video-decoding.force-enabled", false); -#ifdef MOZ_DIRECTSHOW -pref("media.directshow.enabled", true); -pref("media.directshow.h264.active", false); -#endif - #ifdef MOZ_FMP4 pref("media.mp4.enabled", true); // Specifies whether the PDMFactory can create a test decoder that diff --git a/moz.configure b/moz.configure index ec5b027219..f4cbbed1c8 100644 --- a/moz.configure +++ b/moz.configure @@ -5,16 +5,6 @@ include('build/moz.configure/init.configure') -# DirectShow is the legacy Windows media backend. Keep this independent of -# the old configure scripts so the backend is actually compiled on Windows. -@depends(target) -def directshow_enabled(target): - if target.kernel == 'WINNT': - return True - -set_config('MOZ_DIRECTSHOW', directshow_enabled) -set_define('MOZ_DIRECTSHOW', directshow_enabled) - # Note: # - Gecko-specific options and rules should go in toolkit/moz.configure. # - Firefox-specific options and rules should go in browser/moz.configure. diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index a1825c7cad..9684e80ba7 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -337,16 +337,6 @@ var snapshotFormatters = { addRowFromKey("features", "webgl2Extensions"); addRowFromKey("features", "supportsHardwareH264", "hardwareH264"); addRowFromKey("features", "currentAudioBackend", "audioBackend"); - if ("directShowEnabled" in data) { - addRow("features", "directShowEnabled", - strings.GetStringFromName(data.directShowEnabled ? "yes" : "no")); - delete data.directShowEnabled; - } - if ("directShowH264Active" in data) { - addRow("features", "directShowH264Active", - strings.GetStringFromName(data.directShowH264Active ? "yes" : "no")); - delete data.directShowH264Active; - } addRowFromKey("features", "direct2DEnabled", "#Direct2D"); if ("directWriteEnabled" in data) { diff --git a/toolkit/library/moz.build b/toolkit/library/moz.build index 470f10124b..49ff260b47 100644 --- a/toolkit/library/moz.build +++ b/toolkit/library/moz.build @@ -173,9 +173,6 @@ if CONFIG['OS_ARCH'] == 'WINNT': 'crypt32', 'shell32', 'ole32', - 'strmiids', - 'dmoguids', - 'msdmo', 'version', 'winspool', ] diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.properties b/toolkit/locales/en-US/chrome/global/aboutSupport.properties index 629651d81f..b452514158 100644 --- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties +++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties @@ -60,8 +60,6 @@ clearTypeParameters = ClearType Parameters compositing = Compositing hardwareH264 = Hardware H264 Decoding audioBackend = Audio Backend -directShowEnabled = DirectShow H264 Backend Enabled -directShowH264Active = DirectShow H264 Decoding Active mainThreadNoOMTC = main thread, no OMTC yes = Yes no = No diff --git a/toolkit/modules/Troubleshoot.jsm b/toolkit/modules/Troubleshoot.jsm index 1cf7333ed5..c25cbce433 100644 --- a/toolkit/modules/Troubleshoot.jsm +++ b/toolkit/modules/Troubleshoot.jsm @@ -358,8 +358,6 @@ var dataProviders = { } catch (e) {} data.currentAudioBackend = winUtils.currentAudioBackend; - data.directShowEnabled = Services.prefs.getBoolPref("media.directshow.enabled", false); - data.directShowH264Active = Services.prefs.getBoolPref("media.directshow.h264.active", false); if (!data.numAcceleratedWindows && gfxInfo) { #ifdef XP_WIN @@ -553,3 +551,4 @@ var dataProviders = { }); } }; +