mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-27 10:57:34 +09:00
Compare commits
No commits in common. "b5445fd1953446d5b2ff97fa1dc34be1d294394c" and "6569a453d2d1c4fd0feda6ae2791fc03d738005a" have entirely different histories.
b5445fd195
...
6569a453d2
52 changed files with 3047 additions and 921 deletions
10
Makefile.in
10
Makefile.in
|
|
@ -220,14 +220,14 @@ ifneq ($(filter-out maybe_clobber_profiledbuild,$(MAKECMDGOALS)),)
|
||||||
GARBAGE_DIRS += dist _tests
|
GARBAGE_DIRS += dist _tests
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# MSVC PGO builds don't perform a clean before the 2nd pass. So, we want
|
# Windows 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
|
# to preserve content for the 2nd pass on Windows. Everywhere else, we always
|
||||||
# process the install manifests as part of export.
|
# process the install manifests as part of export.
|
||||||
# For the binaries rule, not all the install manifests matter, so force only
|
# For the binaries rule, not all the install manifests matter, so force only
|
||||||
# the interesting ones to be done.
|
# the interesting ones to be done.
|
||||||
ifdef MOZ_PROFILE_USE
|
ifdef MOZ_PROFILE_USE
|
||||||
ifndef NO_PROFILE_GUIDED_OPTIMIZE
|
ifndef NO_PROFILE_GUIDED_OPTIMIZE
|
||||||
ifneq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__)
|
ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||||
recurse_pre-export:: install-manifests
|
recurse_pre-export:: install-manifests
|
||||||
binaries::
|
binaries::
|
||||||
@$(MAKE) install-manifests install_manifests=dist/include
|
@$(MAKE) install-manifests install_manifests=dist/include
|
||||||
|
|
@ -247,10 +247,6 @@ recurse_artifact:
|
||||||
$(topsrcdir)/mach --log-no-times artifact install
|
$(topsrcdir)/mach --log-no-times artifact install
|
||||||
|
|
||||||
ifndef JS_STANDALONE
|
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
|
ifdef ENABLE_TESTS
|
||||||
# Additional makefile targets to call automated test suites
|
# Additional makefile targets to call automated test suites
|
||||||
include $(topsrcdir)/testing/testsuite-targets.mk
|
include $(topsrcdir)/testing/testsuite-targets.mk
|
||||||
|
|
@ -360,7 +356,7 @@ pretty-installer:
|
||||||
|
|
||||||
#XXX: this is a hack, since we don't want to clobber for MSVC
|
#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
|
# PGO support, but we can't do this test in client.mk
|
||||||
ifneq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__)
|
ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||||
# No point in clobbering if PGO has been explicitly disabled.
|
# No point in clobbering if PGO has been explicitly disabled.
|
||||||
ifndef NO_PROFILE_GUIDED_OPTIMIZE
|
ifndef NO_PROFILE_GUIDED_OPTIMIZE
|
||||||
maybe_clobber_profiledbuild: clean
|
maybe_clobber_profiledbuild: clean
|
||||||
|
|
|
||||||
|
|
@ -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 in the Chrome and Edge Web Store (soon)
|
||||||
- Support for extensions released for both Pale Moon, Basilisk and Firefox
|
- Support for extensions released for both Pale Moon, Basilisk and Firefox
|
||||||
- WebExtensions support comparable to Firefox ESR 78
|
- WebExtensions support comparable to Firefox ESR 78
|
||||||
- Literally being the fastest XUL browser (48.7 in Speedometer 2.1 compared to 22.4 in Pale Moon 34.2 AVX2)
|
- Literally being the fastest XUL browser (33.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)
|
- Re-add support for iOS, Android, Windows 2000 and later, NetBSD (soon OpenBSD)
|
||||||
|
|
||||||
## Interested in running Dactyloidae in Win9x?
|
## Interested in running Dactyloidae in Win9x?
|
||||||
|
|
|
||||||
1
aclocal.m4
vendored
1
aclocal.m4
vendored
|
|
@ -17,7 +17,6 @@ builtin(include, build/autoconf/altoptions.m4)dnl
|
||||||
builtin(include, build/autoconf/mozprog.m4)dnl
|
builtin(include, build/autoconf/mozprog.m4)dnl
|
||||||
builtin(include, build/autoconf/mozheader.m4)dnl
|
builtin(include, build/autoconf/mozheader.m4)dnl
|
||||||
builtin(include, build/autoconf/lto.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/frameptr.m4)dnl
|
||||||
builtin(include, build/autoconf/compiler-opts.m4)dnl
|
builtin(include, build/autoconf/compiler-opts.m4)dnl
|
||||||
builtin(include, build/autoconf/expandlibs.m4)dnl
|
builtin(include, build/autoconf/expandlibs.m4)dnl
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
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)
|
|
||||||
])
|
|
||||||
|
|
@ -84,14 +84,7 @@ def get_sdk_dirs(sdk, subdir):
|
||||||
include_dirs['winv6.3'] = include_dirs['include']
|
include_dirs['winv6.3'] = include_dirs['include']
|
||||||
del include_dirs['include']
|
del include_dirs['include']
|
||||||
|
|
||||||
def build_num(v):
|
valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True)
|
||||||
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:
|
if valid_versions:
|
||||||
return namespace(
|
return namespace(
|
||||||
path=sdk,
|
path=sdk,
|
||||||
|
|
@ -143,13 +136,6 @@ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
|
||||||
'WINDOWSSDKDIR (%s). Please verify it contains a valid and '
|
'WINDOWSSDKDIR (%s). Please verify it contains a valid and '
|
||||||
'complete SDK installation.' % windows_sdk_dir_env)
|
'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)
|
valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
|
||||||
if valid_sdks:
|
if valid_sdks:
|
||||||
biggest_version, sdk = sdks[valid_sdks[0]]
|
biggest_version, sdk = sdks[valid_sdks[0]]
|
||||||
|
|
@ -240,7 +226,6 @@ def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env):
|
||||||
'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal '
|
'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal '
|
||||||
'CRT.' % windows_sdk_dir_env)
|
'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)
|
valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
|
||||||
if not valid_sdks:
|
if not valid_sdks:
|
||||||
raise FatalCheckError('Cannot find the Universal CRT SDK. '
|
raise FatalCheckError('Cannot find the Universal CRT SDK. '
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
# 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")
|
|
||||||
|
|
@ -18,20 +18,9 @@ import tempfile
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from mozbuild.base import MozbuildObject
|
from mozbuild.base import MozbuildObject
|
||||||
from buildconfig import substs
|
from buildconfig import substs
|
||||||
from llvm_pgo import LLVMProfile
|
|
||||||
|
|
||||||
PORT = 8888
|
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__':
|
if __name__ == '__main__':
|
||||||
cli = CLI()
|
cli = CLI()
|
||||||
debug_args, interactive = cli.debugger_arguments()
|
debug_args, interactive = cli.debugger_arguments()
|
||||||
|
|
@ -65,15 +54,6 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["XPCOM_DEBUG_BREAK"] = "warn"
|
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
|
# For VC12+, make sure we can find the right bitness of pgort1x0.dll
|
||||||
if not substs.get('HAVE_64BIT_BUILD'):
|
if not substs.get('HAVE_64BIT_BUILD'):
|
||||||
|
|
@ -92,7 +72,7 @@ if __name__ == '__main__':
|
||||||
cmdargs=['javascript:Quitter.quit()'],
|
cmdargs=['javascript:Quitter.quit()'],
|
||||||
env=env)
|
env=env)
|
||||||
runner.start()
|
runner.start()
|
||||||
wait_for_training(runner, 180)
|
runner.wait()
|
||||||
|
|
||||||
jarlog = os.getenv("JARLOG_FILE")
|
jarlog = os.getenv("JARLOG_FILE")
|
||||||
if jarlog:
|
if jarlog:
|
||||||
|
|
@ -105,9 +85,7 @@ if __name__ == '__main__':
|
||||||
cmdargs=cmdargs,
|
cmdargs=cmdargs,
|
||||||
env=env)
|
env=env)
|
||||||
runner.start(debug_args=debug_args, interactive=interactive)
|
runner.start(debug_args=debug_args, interactive=interactive)
|
||||||
wait_for_training(runner, 600)
|
runner.wait()
|
||||||
if llvm_profile:
|
|
||||||
llvm_profile.merge()
|
|
||||||
finally:
|
|
||||||
httpd.stop()
|
httpd.stop()
|
||||||
|
finally:
|
||||||
shutil.rmtree(profilePath)
|
shutil.rmtree(profilePath)
|
||||||
|
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
# 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()
|
|
||||||
|
|
@ -210,25 +210,13 @@ ifdef CPP_UNIT_TESTS
|
||||||
NO_PROFILE_GUIDED_OPTIMIZE = 1
|
NO_PROFILE_GUIDED_OPTIMIZE = 1
|
||||||
endif
|
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
|
# Enable profile-based feedback
|
||||||
ifneq (1,$(NO_PROFILE_GUIDED_OPTIMIZE))
|
ifneq (1,$(NO_PROFILE_GUIDED_OPTIMIZE))
|
||||||
ifdef MOZ_PROFILE_GENERATE
|
ifdef MOZ_PROFILE_GENERATE
|
||||||
OS_CFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_GEN_CFLAGS))
|
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_CXXFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_GEN_CFLAGS))
|
||||||
OS_LDFLAGS += $(PROFILE_GEN_LDFLAGS)
|
OS_LDFLAGS += $(PROFILE_GEN_LDFLAGS)
|
||||||
ifeq ($(OS_ARCH)_$(CLANG_CL),WINNT_)
|
ifeq (WINNT,$(OS_ARCH))
|
||||||
AR_FLAGS += -LTCG
|
AR_FLAGS += -LTCG
|
||||||
endif
|
endif
|
||||||
endif # MOZ_PROFILE_GENERATE
|
endif # MOZ_PROFILE_GENERATE
|
||||||
|
|
@ -237,7 +225,7 @@ ifdef MOZ_PROFILE_USE
|
||||||
OS_CFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_USE_CFLAGS))
|
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_CXXFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_USE_CFLAGS))
|
||||||
OS_LDFLAGS += $(PROFILE_USE_LDFLAGS)
|
OS_LDFLAGS += $(PROFILE_USE_LDFLAGS)
|
||||||
ifeq ($(OS_ARCH)_$(CLANG_CL),WINNT_)
|
ifeq (WINNT,$(OS_ARCH))
|
||||||
AR_FLAGS += -LTCG
|
AR_FLAGS += -LTCG
|
||||||
endif
|
endif
|
||||||
endif # MOZ_PROFILE_USE
|
endif # MOZ_PROFILE_USE
|
||||||
|
|
|
||||||
|
|
@ -531,7 +531,7 @@ endif
|
||||||
##############################################
|
##############################################
|
||||||
ifneq (1,$(NO_PROFILE_GUIDED_OPTIMIZE))
|
ifneq (1,$(NO_PROFILE_GUIDED_OPTIMIZE))
|
||||||
ifdef MOZ_PROFILE_USE
|
ifdef MOZ_PROFILE_USE
|
||||||
ifeq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__)
|
ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||||
# When building with PGO, we have to make sure to re-link
|
# When building with PGO, we have to make sure to re-link
|
||||||
# in the MOZ_PROFILE_USE phase if we linked in the
|
# in the MOZ_PROFILE_USE phase if we linked in the
|
||||||
# MOZ_PROFILE_GENERATE phase. We'll touch this pgo.relink
|
# MOZ_PROFILE_GENERATE phase. We'll touch this pgo.relink
|
||||||
|
|
@ -563,7 +563,7 @@ endif # MOZ_PROFILE_USE
|
||||||
ifdef MOZ_PROFILE_GENERATE
|
ifdef MOZ_PROFILE_GENERATE
|
||||||
# Clean up profiling data during PROFILE_GENERATE phase
|
# Clean up profiling data during PROFILE_GENERATE phase
|
||||||
export::
|
export::
|
||||||
ifeq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__)
|
ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||||
$(foreach pgd,$(wildcard *.pgd),pgomgr -clear $(pgd);)
|
$(foreach pgd,$(wildcard *.pgd),pgomgr -clear $(pgd);)
|
||||||
else
|
else
|
||||||
ifdef GNU_CC
|
ifdef GNU_CC
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,11 @@
|
||||||
#include "FlacDecoder.h"
|
#include "FlacDecoder.h"
|
||||||
#include "FlacDemuxer.h"
|
#include "FlacDemuxer.h"
|
||||||
|
|
||||||
|
#ifdef MOZ_DIRECTSHOW
|
||||||
|
#include "DirectShowDecoder.h"
|
||||||
|
#include "DirectShowReader.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "nsPluginHost.h"
|
#include "nsPluginHost.h"
|
||||||
#include "MediaPrefs.h"
|
#include "MediaPrefs.h"
|
||||||
|
|
||||||
|
|
@ -138,6 +143,14 @@ IsFlacSupportedType(const nsACString& aType,
|
||||||
return FlacDecoder::CanHandleMediaType(aType, aCodecs);
|
return FlacDecoder::CanHandleMediaType(aType, aCodecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MOZ_DIRECTSHOW
|
||||||
|
static bool
|
||||||
|
IsDirectShowSupportedType(const nsACString& aType)
|
||||||
|
{
|
||||||
|
return DirectShowDecoder::GetSupportedCodecs(aType, nullptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static
|
static
|
||||||
CanPlayStatus
|
CanPlayStatus
|
||||||
CanHandleCodecsType(const MediaContentType& aType,
|
CanHandleCodecsType(const MediaContentType& aType,
|
||||||
|
|
@ -197,6 +210,11 @@ CanHandleCodecsType(const MediaContentType& aType,
|
||||||
if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) {
|
if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) {
|
||||||
return CANPLAY_YES;
|
return CANPLAY_YES;
|
||||||
}
|
}
|
||||||
|
#ifdef MOZ_DIRECTSHOW
|
||||||
|
if (IsDirectShowSupportedType(aType.GetMIMEType())) {
|
||||||
|
return CANPLAY_YES;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (!codecList) {
|
if (!codecList) {
|
||||||
return CANPLAY_MAYBE;
|
return CANPLAY_MAYBE;
|
||||||
}
|
}
|
||||||
|
|
@ -258,6 +276,11 @@ CanHandleMediaType(const MediaContentType& aType,
|
||||||
if (IsFlacSupportedType(aType.GetMIMEType())) {
|
if (IsFlacSupportedType(aType.GetMIMEType())) {
|
||||||
return CANPLAY_MAYBE;
|
return CANPLAY_MAYBE;
|
||||||
}
|
}
|
||||||
|
#ifdef MOZ_DIRECTSHOW
|
||||||
|
if (IsDirectShowSupportedType(aType.GetMIMEType())) {
|
||||||
|
return CANPLAY_MAYBE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return CANPLAY_NO;
|
return CANPLAY_NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -347,6 +370,13 @@ InstantiateDecoder(const nsACString& aType,
|
||||||
return decoder.forget();
|
return decoder.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MOZ_DIRECTSHOW
|
||||||
|
if (IsDirectShowSupportedType(aType)) {
|
||||||
|
decoder = new DirectShowDecoder(aOwner);
|
||||||
|
return decoder.forget();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -393,6 +423,11 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
|
||||||
decoderReader =
|
decoderReader =
|
||||||
new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource()));
|
new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource()));
|
||||||
}
|
}
|
||||||
|
#ifdef MOZ_DIRECTSHOW
|
||||||
|
else if (IsDirectShowSupportedType(aType)) {
|
||||||
|
decoderReader = new DirectShowReader(aDecoder);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return decoderReader;
|
return decoderReader;
|
||||||
}
|
}
|
||||||
|
|
@ -418,6 +453,9 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
|
||||||
IsAACSupportedType(aType) ||
|
IsAACSupportedType(aType) ||
|
||||||
IsWaveSupportedType(aType) ||
|
IsWaveSupportedType(aType) ||
|
||||||
IsFlacSupportedType(aType) ||
|
IsFlacSupportedType(aType) ||
|
||||||
|
#ifdef MOZ_DIRECTSHOW
|
||||||
|
IsDirectShowSupportedType(aType) ||
|
||||||
|
#endif
|
||||||
false;
|
false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
290
dom/media/directshow/AudioSinkFilter.cpp
Normal file
290
dom/media/directshow/AudioSinkFilter.cpp
Normal file
|
|
@ -0,0 +1,290 @@
|
||||||
|
/* -*- 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 <initguid.h>
|
||||||
|
#include <wmsdkidl.h>
|
||||||
|
|
||||||
|
#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<BasePin*>(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<IMediaSeeking*>(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<IMediaSeeking> 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
|
||||||
96
dom/media/directshow/AudioSinkFilter.h
Normal file
96
dom/media/directshow/AudioSinkFilter.h
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
/* -*- 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<AudioSinkInputPin> mInputPin;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // AudioSinkFilter_h_
|
||||||
210
dom/media/directshow/AudioSinkInputPin.cpp
Normal file
210
dom/media/directshow/AudioSinkInputPin.cpp
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
/* -*- 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 <wmsdkidl.h>
|
||||||
|
|
||||||
|
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<AudioSinkFilter*>(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<IMediaSeeking>
|
||||||
|
AudioSinkInputPin::GetConnectedPinSeeking()
|
||||||
|
{
|
||||||
|
RefPtr<IPin> peer = GetConnected();
|
||||||
|
if (!peer)
|
||||||
|
return nullptr;
|
||||||
|
RefPtr<IMediaSeeking> seeking;
|
||||||
|
peer->QueryInterface(static_cast<IMediaSeeking**>(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
|
||||||
78
dom/media/directshow/AudioSinkInputPin.h
Normal file
78
dom/media/directshow/AudioSinkInputPin.h
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* -*- 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<IMediaSeeking> 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<SampleSink> mSampleSink;
|
||||||
|
|
||||||
|
// Synchronized by the filter lock; BaseInputPin::mLock.
|
||||||
|
REFERENCE_TIME mSegmentStartTime;
|
||||||
|
VIDEOINFOHEADER mVideoInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // AudioSinkInputPin_h_
|
||||||
78
dom/media/directshow/DirectShowDecoder.cpp
Normal file
78
dom/media/directshow/DirectShowDecoder.cpp
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* -*- 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
|
||||||
45
dom/media/directshow/DirectShowDecoder.h
Normal file
45
dom/media/directshow/DirectShowDecoder.h
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
/* -*- 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
|
||||||
417
dom/media/directshow/DirectShowReader.cpp
Normal file
417
dom/media/directshow/DirectShowReader.cpp
Normal file
|
|
@ -0,0 +1,417 @@
|
||||||
|
/* -*- 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<void**>(static_cast<IGraphBuilder**>(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<IMediaControl**>(getter_AddRefs(mControl)));
|
||||||
|
NS_ENSURE_TRUE(SUCCEEDED(hr) && mControl, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
hr = mGraph->QueryInterface(static_cast<IMediaSeeking**>(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<IBaseFilter> demuxer;
|
||||||
|
hr = CreateAndAddFilter(mGraph,
|
||||||
|
CLSID_MPEG1Splitter,
|
||||||
|
L"MPEG1Splitter",
|
||||||
|
getter_AddRefs(demuxer));
|
||||||
|
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
// Platform MP3 decoder.
|
||||||
|
RefPtr<IBaseFilter> 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<IBaseFilter> 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<IMediaEventSink> eventSink;
|
||||||
|
HRESULT hr = mGraph->QueryInterface(static_cast<IMediaEventSink**>(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<int16_t *>(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<IMediaSample> 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<uint8_t *>(data),
|
||||||
|
mBytesPerSample,
|
||||||
|
numSamples,
|
||||||
|
mNumChannels));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
DirectShowReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||||
|
int64_t aTimeThreshold)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
|
if (!mIsH264) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
RefPtr<IMediaSample> 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<uint8_t> y;
|
||||||
|
nsTArray<uint8_t> u;
|
||||||
|
nsTArray<uint8_t> 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<VideoData> 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<MediaDecoderReader::SeekPromise>
|
||||||
|
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
|
||||||
106
dom/media/directshow/DirectShowReader.h
Normal file
106
dom/media/directshow/DirectShowReader.h
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
/* -*- 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<SeekPromise>
|
||||||
|
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<IGraphBuilder> mGraph;
|
||||||
|
RefPtr<IMediaControl> mControl;
|
||||||
|
RefPtr<IMediaSeeking> mMediaSeeking;
|
||||||
|
|
||||||
|
// Wraps the MediaResource, and feeds undecoded data into the filter graph.
|
||||||
|
RefPtr<SourceFilter> 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<AudioSinkFilter> 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
|
||||||
457
dom/media/directshow/DirectShowUtils.cpp
Normal file
457
dom/media/directshow/DirectShowUtils.cpp
Normal file
|
|
@ -0,0 +1,457 @@
|
||||||
|
/* 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 <wchar.h>
|
||||||
|
|
||||||
|
#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 <uuids.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
#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<IRunningObjectTable> runningObjectTable;
|
||||||
|
if (SUCCEEDED(GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)))) {
|
||||||
|
runningObjectTable->Revoke(aRotRegister);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
RefPtr<IMoniker> moniker;
|
||||||
|
RefPtr<IRunningObjectTable> 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<IBaseFilter> 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<IBaseFilter> 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<IDMOWrapperFilter> 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<IBaseFilter> 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<IBaseFilter> 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<ICreateDevEnum> devEnum;
|
||||||
|
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr,
|
||||||
|
CLSCTX_INPROC_SERVER,
|
||||||
|
IID_ICreateDevEnum, getter_AddRefs(devEnum));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<IEnumMoniker> filters;
|
||||||
|
hr = devEnum->CreateClassEnumerator(CLSID_LegacyAmFilterCategory,
|
||||||
|
getter_AddRefs(filters), 0);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<IMoniker> moniker;
|
||||||
|
ULONG fetched = 0;
|
||||||
|
while (filters->Next(1, getter_AddRefs(moniker), &fetched) == S_OK) {
|
||||||
|
RefPtr<IPropertyBag> 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<ICreateDevEnum> devEnum;
|
||||||
|
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr,
|
||||||
|
CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
|
||||||
|
getter_AddRefs(devEnum));
|
||||||
|
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
||||||
|
RefPtr<IEnumMoniker> filters;
|
||||||
|
hr = devEnum->CreateClassEnumerator(CLSID_LegacyAmFilterCategory,
|
||||||
|
getter_AddRefs(filters), 0);
|
||||||
|
NS_ENSURE_TRUE(hr == S_OK, VFW_E_NOT_FOUND);
|
||||||
|
RefPtr<IMoniker> moniker;
|
||||||
|
ULONG fetched = 0;
|
||||||
|
while (filters->Next(1, getter_AddRefs(moniker), &fetched) == S_OK) {
|
||||||
|
RefPtr<IPropertyBag> 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<void**>(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<IPin> 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<IPin>
|
||||||
|
GetUnconnectedPin(IBaseFilter* aFilter, PIN_DIRECTION aPinDir)
|
||||||
|
{
|
||||||
|
RefPtr<IEnumPins> 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<IPin> 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<IPin> output = GetUnconnectedPin(aOutputFilter, PINDIR_OUTPUT);
|
||||||
|
NS_ENSURE_TRUE(output, E_FAIL);
|
||||||
|
|
||||||
|
RefPtr<IPin> 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
|
||||||
134
dom/media/directshow/DirectShowUtils.h
Normal file
134
dom/media/directshow/DirectShowUtils.h
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
/* 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 <stdint.h>
|
||||||
|
#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
|
||||||
159
dom/media/directshow/SampleSink.cpp
Normal file
159
dom/media/directshow/SampleSink.cpp
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
/* -*- 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<IMediaSample>& 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
|
||||||
|
|
||||||
67
dom/media/directshow/SampleSink.h
Normal file
67
dom/media/directshow/SampleSink.h
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* -*- 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<IMediaSample>& 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<IMediaSample> mSample;
|
||||||
|
|
||||||
|
// Format of the audio stream we're receiving.
|
||||||
|
WAVEFORMATEX mAudioFormat;
|
||||||
|
|
||||||
|
bool mIsFlushing;
|
||||||
|
bool mAtEOS;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // SampleSink_h_
|
||||||
682
dom/media/directshow/SourceFilter.cpp
Normal file
682
dom/media/directshow/SourceFilter.cpp
Normal file
|
|
@ -0,0 +1,682 @@
|
||||||
|
/* -*- 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 <algorithm>
|
||||||
|
|
||||||
|
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<IMediaSample> 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<int64_t>(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<BaseFilter*>(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<IAsyncReader*>(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<IMemAllocator> 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<ReadRequest> request(reinterpret_cast<ReadRequest*>(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<char*>(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<BasePin*>(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
|
||||||
75
dom/media/directshow/SourceFilter.h
Normal file
75
dom/media/directshow/SourceFilter.h
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/* -*- 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<OutputPin> 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
|
||||||
42
dom/media/directshow/moz.build
Normal file
42
dom/media/directshow/moz.build
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# -*- 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',
|
||||||
|
]
|
||||||
|
|
@ -42,6 +42,12 @@ DIRS += [
|
||||||
'standalone',
|
'standalone',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if CONFIG['MOZ_DIRECTSHOW']:
|
||||||
|
DIRS += ['directshow']
|
||||||
|
LOCAL_INCLUDES += [
|
||||||
|
'/media/webrtc/trunk/webrtc/modules/video_capture/windows',
|
||||||
|
]
|
||||||
|
|
||||||
if CONFIG['MOZ_FMP4']:
|
if CONFIG['MOZ_FMP4']:
|
||||||
DIRS += ['fmp4']
|
DIRS += ['fmp4']
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include "TextureD3D11.h"
|
#include "TextureD3D11.h"
|
||||||
#include "CompositorD3D11Shaders.h"
|
#include "CompositorD3D11Shaders.h"
|
||||||
#include "GpuRasterD3D11.h"
|
|
||||||
|
|
||||||
#include "gfxWindowsPlatform.h"
|
#include "gfxWindowsPlatform.h"
|
||||||
#include "nsIWidget.h"
|
#include "nsIWidget.h"
|
||||||
|
|
@ -144,7 +143,6 @@ private:
|
||||||
|
|
||||||
CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
|
CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
|
||||||
: Compositor(aWidget, aParent)
|
: Compositor(aWidget, aParent)
|
||||||
, mGpuRasterViewport(false)
|
|
||||||
, mAttachments(nullptr)
|
, mAttachments(nullptr)
|
||||||
, mHwnd(nullptr)
|
, mHwnd(nullptr)
|
||||||
, mDisableSequenceForNextFrame(false)
|
, mDisableSequenceForNextFrame(false)
|
||||||
|
|
@ -201,16 +199,6 @@ CompositorD3D11::Initialize(nsCString* const out_failureReason)
|
||||||
|
|
||||||
mFeatureLevel = mDevice->GetFeatureLevel();
|
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();
|
mHwnd = mWidget->AsWindows()->GetHwnd();
|
||||||
|
|
||||||
memset(&mVSConstants, 0, sizeof(VertexShaderConstants));
|
memset(&mVSConstants, 0, sizeof(VertexShaderConstants));
|
||||||
|
|
@ -668,20 +656,6 @@ CompositorD3D11::GetPSForEffect(Effect* aEffect, MaskType aMaskType)
|
||||||
void
|
void
|
||||||
CompositorD3D11::ClearRect(const gfx::Rect& aRect)
|
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);
|
mContext->OMSetBlendState(mAttachments->mDisabledBlendState, sBlendFactor, 0xFFFFFFFF);
|
||||||
|
|
||||||
Matrix4x4 identity;
|
Matrix4x4 identity;
|
||||||
|
|
@ -762,39 +736,6 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
||||||
|
|
||||||
MOZ_ASSERT(mCurrentRT, "No render target");
|
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<EffectSolidColor*>(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);
|
memcpy(&mVSConstants.layerTransform, &aTransform._11, 64);
|
||||||
IntPoint origin = mCurrentRT->GetOrigin();
|
IntPoint origin = mCurrentRT->GetOrigin();
|
||||||
mVSConstants.renderTargetOffset[0] = origin.x;
|
mVSConstants.renderTargetOffset[0] = origin.x;
|
||||||
|
|
@ -1252,7 +1193,6 @@ CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize)
|
||||||
projection._33 = 0.0f;
|
projection._33 = 0.0f;
|
||||||
|
|
||||||
PrepareViewport(aSize, projection, 0.0f, 1.0f);
|
PrepareViewport(aSize, projection, 0.0f, 1.0f);
|
||||||
mGpuRasterViewport = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -1273,7 +1213,6 @@ CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize,
|
||||||
const gfx::Matrix4x4& aProjection,
|
const gfx::Matrix4x4& aProjection,
|
||||||
float aZNear, float aZFar)
|
float aZNear, float aZFar)
|
||||||
{
|
{
|
||||||
mGpuRasterViewport = false;
|
|
||||||
D3D11_VIEWPORT viewport;
|
D3D11_VIEWPORT viewport;
|
||||||
viewport.MaxDepth = aZFar;
|
viewport.MaxDepth = aZFar;
|
||||||
viewport.MinDepth = aZNear;
|
viewport.MinDepth = aZNear;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
#include "mozilla/layers/Compositor.h"
|
#include "mozilla/layers/Compositor.h"
|
||||||
#include "TextureD3D11.h"
|
#include "TextureD3D11.h"
|
||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
#include <d3d11_1.h>
|
|
||||||
|
|
||||||
class nsWidget;
|
class nsWidget;
|
||||||
|
|
||||||
|
|
@ -175,8 +174,6 @@ private:
|
||||||
RefPtr<ID3D11ShaderResourceView>* aOutView);
|
RefPtr<ID3D11ShaderResourceView>* aOutView);
|
||||||
|
|
||||||
RefPtr<ID3D11DeviceContext> mContext;
|
RefPtr<ID3D11DeviceContext> mContext;
|
||||||
RefPtr<ID3D11DeviceContext1> mGpuRasterContext;
|
|
||||||
bool mGpuRasterViewport;
|
|
||||||
RefPtr<ID3D11Device> mDevice;
|
RefPtr<ID3D11Device> mDevice;
|
||||||
RefPtr<IDXGISwapChain> mSwapChain;
|
RefPtr<IDXGISwapChain> mSwapChain;
|
||||||
RefPtr<CompositingRenderTargetD3D11> mDefaultRT;
|
RefPtr<CompositingRenderTargetD3D11> mDefaultRT;
|
||||||
|
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
/* 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 <limits.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
/* 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 <d3d11_1.h>
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
@ -80,7 +80,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||||
]
|
]
|
||||||
SOURCES += [
|
SOURCES += [
|
||||||
'd3d11/CompositorD3D11.cpp',
|
'd3d11/CompositorD3D11.cpp',
|
||||||
'd3d11/GpuRasterD3D11.c',
|
|
||||||
'd3d11/ReadbackManagerD3D11.cpp',
|
'd3d11/ReadbackManagerD3D11.cpp',
|
||||||
]
|
]
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
|
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
/* 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 <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
#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<double>::quiet_NaN();
|
|
||||||
CHECK_GPU(!GpuRasterD3D11ClipRect(&rect, &clip, 10, 10, &output));
|
|
||||||
rect.left = std::numeric_limits<double>::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<const uint32_t*>(
|
|
||||||
static_cast<const uint8_t*>(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
|
|
||||||
|
|
@ -53,9 +53,6 @@ LOCAL_INCLUDES += [
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul-gtest'
|
FINAL_LIBRARY = 'xul-gtest'
|
||||||
|
|
||||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows' and CONFIG['MOZ_ENABLE_D3D10_LAYER']:
|
|
||||||
SOURCES += ['TestGpuRasterD3D11.cpp']
|
|
||||||
|
|
||||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||||
|
|
||||||
if CONFIG['GNU_CXX']:
|
if CONFIG['GNU_CXX']:
|
||||||
|
|
|
||||||
|
|
@ -468,7 +468,6 @@ private:
|
||||||
DECL_GFX_PREF(Once, "layers.componentalpha.enabled", ComponentAlphaEnabled, bool, true);
|
DECL_GFX_PREF(Once, "layers.componentalpha.enabled", ComponentAlphaEnabled, bool, true);
|
||||||
DECL_GFX_PREF(Live, "layers.composer2d.enabled", Composer2DCompositionEnabled, bool, false);
|
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.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.deaa.enabled", LayersDEAAEnabled, bool, false);
|
||||||
DECL_GFX_PREF(Live, "layers.draw-bigimage-borders", DrawBigImageBorders, bool, false);
|
DECL_GFX_PREF(Live, "layers.draw-bigimage-borders", DrawBigImageBorders, bool, false);
|
||||||
DECL_GFX_PREF(Live, "layers.draw-borders", DrawLayerBorders, bool, false);
|
DECL_GFX_PREF(Live, "layers.draw-borders", DrawLayerBorders, bool, false);
|
||||||
|
|
|
||||||
1
js/src/aclocal.m4
vendored
1
js/src/aclocal.m4
vendored
|
|
@ -16,7 +16,6 @@ builtin(include, ../../build/autoconf/altoptions.m4)dnl
|
||||||
builtin(include, ../../build/autoconf/mozprog.m4)dnl
|
builtin(include, ../../build/autoconf/mozprog.m4)dnl
|
||||||
builtin(include, ../../build/autoconf/mozheader.m4)dnl
|
builtin(include, ../../build/autoconf/mozheader.m4)dnl
|
||||||
builtin(include, ../../build/autoconf/lto.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/frameptr.m4)dnl
|
||||||
builtin(include, ../../build/autoconf/compiler-opts.m4)dnl
|
builtin(include, ../../build/autoconf/compiler-opts.m4)dnl
|
||||||
builtin(include, ../../build/autoconf/expandlibs.m4)dnl
|
builtin(include, ../../build/autoconf/expandlibs.m4)dnl
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
@ -17,7 +17,6 @@ UNIFIED_SOURCES += [
|
||||||
'testBug604087.cpp',
|
'testBug604087.cpp',
|
||||||
'testCallArgs.cpp',
|
'testCallArgs.cpp',
|
||||||
'testCallNonGenericMethodOnProxy.cpp',
|
'testCallNonGenericMethodOnProxy.cpp',
|
||||||
'testCharacterOperations.cpp',
|
|
||||||
'testChromeBuffer.cpp',
|
'testChromeBuffer.cpp',
|
||||||
'testClassGetter.cpp',
|
'testClassGetter.cpp',
|
||||||
'testCloneScript.cpp',
|
'testCloneScript.cpp',
|
||||||
|
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
/* 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 <stdio.h>
|
|
||||||
|
|
||||||
#ifndef JS_CHARACTER_OPERATIONS_STANDALONE
|
|
||||||
# include "jsapi-tests/tests.h"
|
|
||||||
# include "jswin.h"
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
# include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename CharT>
|
|
||||||
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<char*>(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<char16_t*>(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<char>() && CheckCharacterSearch<unsigned char>() &&
|
|
||||||
CheckCharacterSearch<char16_t>() && 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
|
|
||||||
|
|
@ -38,7 +38,6 @@
|
||||||
#include "js/UniquePtr.h"
|
#include "js/UniquePtr.h"
|
||||||
#include "unicode/uchar.h"
|
#include "unicode/uchar.h"
|
||||||
#include "unicode/unorm2.h"
|
#include "unicode/unorm2.h"
|
||||||
#include "vm/CharacterOperations.h"
|
|
||||||
#include "vm/GlobalObject.h"
|
#include "vm/GlobalObject.h"
|
||||||
#include "vm/Interpreter.h"
|
#include "vm/Interpreter.h"
|
||||||
#include "vm/Opcodes.h"
|
#include "vm/Opcodes.h"
|
||||||
|
|
@ -1641,11 +1640,7 @@ FirstCharMatcherUnrolled(const TextChar* text, uint32_t n, const PatChar pat)
|
||||||
static const char*
|
static const char*
|
||||||
FirstCharMatcher8bit(const char* text, uint32_t n, const char pat)
|
FirstCharMatcher8bit(const char* text, uint32_t n, const char pat)
|
||||||
{
|
{
|
||||||
#ifdef JS_HAS_SSE2_CHARACTER_OPERATIONS
|
#if defined(__clang__)
|
||||||
if (n >= 16)
|
|
||||||
return FindCharacter(text, n, pat);
|
|
||||||
#endif
|
|
||||||
#if defined(__clang__)
|
|
||||||
return FirstCharMatcherUnrolled<char, char>(text, n, pat);
|
return FirstCharMatcherUnrolled<char, char>(text, n, pat);
|
||||||
#else
|
#else
|
||||||
return reinterpret_cast<const char*>(memchr(text, pat, n));
|
return reinterpret_cast<const char*>(memchr(text, pat, n));
|
||||||
|
|
@ -1655,10 +1650,6 @@ FirstCharMatcher8bit(const char* text, uint32_t n, const char pat)
|
||||||
static const char16_t*
|
static const char16_t*
|
||||||
FirstCharMatcher16bit(const char16_t* text, uint32_t n, const char16_t pat)
|
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)
|
#if defined(XP_DARWIN) || defined(XP_WIN)
|
||||||
/*
|
/*
|
||||||
* Performance of memchr is horrible in OSX. Windows is better,
|
* Performance of memchr is horrible in OSX. Windows is better,
|
||||||
|
|
@ -1743,14 +1734,18 @@ StringMatch(const TextChar* text, uint32_t textLen, const PatChar* pat, uint32_t
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
#if defined(__i386__) || defined(_M_IX86) || defined(__i386)
|
#if defined(__i386__) || defined(_M_IX86) || defined(__i386)
|
||||||
// Avoid the generic substring matcher for a single character on x86.
|
/*
|
||||||
// FindCharacter uses SSE2 where available, including mixed encodings.
|
* Given enough registers, the unrolled loop below is faster than the
|
||||||
|
* following loop. 32-bit x86 does not have enough registers.
|
||||||
|
*/
|
||||||
if (patLen == 1) {
|
if (patLen == 1) {
|
||||||
// A two-byte needle cannot match Latin1 text if it exceeds 0xff.
|
const PatChar p0 = *pat;
|
||||||
if (sizeof(TextChar) == 1 && uint32_t(*pat) > 0xff)
|
const TextChar* end = text + textLen;
|
||||||
return -1;
|
for (const TextChar* c = text; c != end; ++c) {
|
||||||
const TextChar* match = FindCharacter(text, textLen, TextChar(*pat));
|
if (*c == p0)
|
||||||
return match ? int(match - text) : -1;
|
return c - text;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -711,17 +711,7 @@ case "$target" in
|
||||||
if test "$AS_BIN"; then
|
if test "$AS_BIN"; then
|
||||||
AS="$(basename "$AS_BIN")"
|
AS="$(basename "$AS_BIN")"
|
||||||
fi
|
fi
|
||||||
if test -n "$CLANG_CL"; then
|
AR='lib'
|
||||||
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_FLAGS='-NOLOGO -OUT:$@'
|
||||||
AR_EXTRACT=
|
AR_EXTRACT=
|
||||||
RANLIB='echo not_ranlib'
|
RANLIB='echo not_ranlib'
|
||||||
|
|
@ -2015,9 +2005,6 @@ if test "$ac_cv_struct_tm_zone_tm_gmtoff" = "yes" ; then
|
||||||
fi
|
fi
|
||||||
fi # ! SKIP_COMPILER_CHECKS
|
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_DEFINE(CPP_THROW_NEW, [throw()])
|
||||||
AC_LANG_C
|
AC_LANG_C
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
/* 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 <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
// 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 <emmintrin.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
|
|
||||||
template <typename CharT>
|
|
||||||
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<char>(match))
|
|
||||||
: _mm_set1_epi16(static_cast<short>(match));
|
|
||||||
do {
|
|
||||||
// Never read beyond the supplied span, even at a page boundary.
|
|
||||||
const __m128i block = _mm_loadu_si128(reinterpret_cast<const __m128i*>(chars));
|
|
||||||
const __m128i equal = sizeof(CharT) == 1
|
|
||||||
? _mm_cmpeq_epi8(block, needle)
|
|
||||||
: _mm_cmpeq_epi16(block, needle);
|
|
||||||
const uint32_t mask = static_cast<uint32_t>(_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<short>(0xff00));
|
|
||||||
const __m128i zero = _mm_setzero_si128();
|
|
||||||
do {
|
|
||||||
const __m128i block = _mm_loadu_si128(reinterpret_cast<const __m128i*>(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
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "js/UbiNode.h"
|
#include "js/UbiNode.h"
|
||||||
#include "vm/CharacterOperations.h"
|
|
||||||
#include "vm/SPSProfiler.h"
|
#include "vm/SPSProfiler.h"
|
||||||
|
|
||||||
#include "jscntxtinlines.h"
|
#include "jscntxtinlines.h"
|
||||||
|
|
@ -1148,8 +1147,12 @@ js::NewDependentString(JSContext* cx, JSString* baseArg, size_t start, size_t le
|
||||||
static bool
|
static bool
|
||||||
CanStoreCharsAsLatin1(const char16_t* s, size_t length)
|
CanStoreCharsAsLatin1(const char16_t* s, size_t length)
|
||||||
{
|
{
|
||||||
static_assert(JSString::MAX_LATIN1_CHAR == 0xff, "Latin1 character range");
|
for (const char16_t* end = s + length; s < end; ++s) {
|
||||||
return CharactersFitInLatin1(s, length);
|
if (*s > JSString::MAX_LATIN1_CHAR)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,6 @@
|
||||||
|
|
||||||
#define MOZALLOC_HAVE_XMALLOC
|
#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 */
|
/* Workaround build problem with Sun Studio 12 */
|
||||||
#if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
|
#if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
|
||||||
# undef MOZ_MUST_USE
|
# undef MOZ_MUST_USE
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,11 @@ pref("media.wave.play-stand-alone", false);
|
||||||
pref("media.hardware-video-decoding.enabled", true);
|
pref("media.hardware-video-decoding.enabled", true);
|
||||||
pref("media.hardware-video-decoding.force-enabled", false);
|
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
|
#ifdef MOZ_FMP4
|
||||||
pref("media.mp4.enabled", true);
|
pref("media.mp4.enabled", true);
|
||||||
// Specifies whether the PDMFactory can create a test decoder that
|
// Specifies whether the PDMFactory can create a test decoder that
|
||||||
|
|
@ -4565,8 +4570,6 @@ pref("gfx.direct2d.force-enabled", false);
|
||||||
|
|
||||||
pref("layers.prefer-opengl", false);
|
pref("layers.prefer-opengl", false);
|
||||||
pref("layers.prefer-d3d9", 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
|
// Enable fallback if d3d11 can't be used. See bug #1262187
|
||||||
pref("layers.allow-d3d9-fallback", true);
|
pref("layers.allow-d3d9-fallback", true);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,16 @@
|
||||||
|
|
||||||
include('build/moz.configure/init.configure')
|
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:
|
# Note:
|
||||||
# - Gecko-specific options and rules should go in toolkit/moz.configure.
|
# - Gecko-specific options and rules should go in toolkit/moz.configure.
|
||||||
# - Firefox-specific options and rules should go in browser/moz.configure.
|
# - Firefox-specific options and rules should go in browser/moz.configure.
|
||||||
|
|
|
||||||
|
|
@ -876,17 +876,7 @@ case "$target" in
|
||||||
if test "$AS_BIN"; then
|
if test "$AS_BIN"; then
|
||||||
AS="$(basename "$AS_BIN")"
|
AS="$(basename "$AS_BIN")"
|
||||||
fi
|
fi
|
||||||
if test -n "$CLANG_CL"; then
|
AR='lib'
|
||||||
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_FLAGS='-NOLOGO -OUT:$@'
|
||||||
AR_EXTRACT=
|
AR_EXTRACT=
|
||||||
RANLIB='echo not_ranlib'
|
RANLIB='echo not_ranlib'
|
||||||
|
|
@ -4557,9 +4547,6 @@ AC_SUBST(PROFILE_USE_LDFLAGS)
|
||||||
|
|
||||||
fi # ! SKIP_COMPILER_CHECKS
|
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_DEFINE(CPP_THROW_NEW, [throw()])
|
||||||
AC_LANG_C
|
AC_LANG_C
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,9 @@ cppunittests-remote:
|
||||||
jetpack-tests:
|
jetpack-tests:
|
||||||
cd $(topsrcdir)/addon-sdk/source && $(PYTHON) bin/cfx -b $(abspath $(browser_path)) --parseable testpkgs
|
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
|
# Package up the tests and test harnesses
|
||||||
include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
|
include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -337,6 +337,16 @@ var snapshotFormatters = {
|
||||||
addRowFromKey("features", "webgl2Extensions");
|
addRowFromKey("features", "webgl2Extensions");
|
||||||
addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
|
addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
|
||||||
addRowFromKey("features", "currentAudioBackend", "audioBackend");
|
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");
|
addRowFromKey("features", "direct2DEnabled", "#Direct2D");
|
||||||
|
|
||||||
if ("directWriteEnabled" in data) {
|
if ("directWriteEnabled" in data) {
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,9 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||||
'crypt32',
|
'crypt32',
|
||||||
'shell32',
|
'shell32',
|
||||||
'ole32',
|
'ole32',
|
||||||
|
'strmiids',
|
||||||
|
'dmoguids',
|
||||||
|
'msdmo',
|
||||||
'version',
|
'version',
|
||||||
'winspool',
|
'winspool',
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ clearTypeParameters = ClearType Parameters
|
||||||
compositing = Compositing
|
compositing = Compositing
|
||||||
hardwareH264 = Hardware H264 Decoding
|
hardwareH264 = Hardware H264 Decoding
|
||||||
audioBackend = Audio Backend
|
audioBackend = Audio Backend
|
||||||
|
directShowEnabled = DirectShow H264 Backend Enabled
|
||||||
|
directShowH264Active = DirectShow H264 Decoding Active
|
||||||
mainThreadNoOMTC = main thread, no OMTC
|
mainThreadNoOMTC = main thread, no OMTC
|
||||||
yes = Yes
|
yes = Yes
|
||||||
no = No
|
no = No
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,8 @@ var dataProviders = {
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
data.currentAudioBackend = winUtils.currentAudioBackend;
|
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) {
|
if (!data.numAcceleratedWindows && gfxInfo) {
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
|
|
@ -551,4 +553,3 @@ var dataProviders = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue