mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-20 15:27:32 +09:00
Compare commits
7 commits
6569a453d2
...
b5445fd195
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5445fd195 | ||
|
|
5ec85f3abe | ||
|
|
c6ed045774 | ||
|
|
f317c71c16 |
||
|
|
25c4eb5efb | ||
|
|
b7c0ebfcd4 | ||
|
|
70b4a79405 |
52 changed files with 921 additions and 3047 deletions
10
Makefile.in
10
Makefile.in
|
|
@ -220,14 +220,14 @@ ifneq ($(filter-out maybe_clobber_profiledbuild,$(MAKECMDGOALS)),)
|
|||
GARBAGE_DIRS += dist _tests
|
||||
endif
|
||||
|
||||
# Windows PGO builds don't perform a clean before the 2nd pass. So, we want
|
||||
# MSVC PGO builds don't perform a clean before the 2nd pass. So, we want
|
||||
# to preserve content for the 2nd pass on Windows. Everywhere else, we always
|
||||
# process the install manifests as part of export.
|
||||
# For the binaries rule, not all the install manifests matter, so force only
|
||||
# the interesting ones to be done.
|
||||
ifdef MOZ_PROFILE_USE
|
||||
ifndef NO_PROFILE_GUIDED_OPTIMIZE
|
||||
ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||
ifneq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__)
|
||||
recurse_pre-export:: install-manifests
|
||||
binaries::
|
||||
@$(MAKE) install-manifests install_manifests=dist/include
|
||||
|
|
@ -247,6 +247,10 @@ recurse_artifact:
|
|||
$(topsrcdir)/mach --log-no-times artifact install
|
||||
|
||||
ifndef JS_STANDALONE
|
||||
# PGO training is also needed in release builds configured without tests.
|
||||
pgo-profile-run:
|
||||
$(PYTHON) $(topsrcdir)/build/pgo/profileserver.py $(EXTRA_TEST_ARGS)
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
# Additional makefile targets to call automated test suites
|
||||
include $(topsrcdir)/testing/testsuite-targets.mk
|
||||
|
|
@ -356,7 +360,7 @@ pretty-installer:
|
|||
|
||||
#XXX: this is a hack, since we don't want to clobber for MSVC
|
||||
# PGO support, but we can't do this test in client.mk
|
||||
ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||
ifneq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__)
|
||||
# No point in clobbering if PGO has been explicitly disabled.
|
||||
ifndef NO_PROFILE_GUIDED_OPTIMIZE
|
||||
maybe_clobber_profiledbuild: clean
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Some advantages over upstream, roytam1's Serpent, Eclipse Hydra and Firefox are:
|
|||
- Support for extensions in the Chrome and Edge Web Store (soon)
|
||||
- Support for extensions released for both Pale Moon, Basilisk and Firefox
|
||||
- WebExtensions support comparable to Firefox ESR 78
|
||||
- Literally being the fastest XUL browser (33.7 in Speedometer 2.1 compared to 22.4 in Pale Moon 34.2 AVX2)
|
||||
- Literally being the fastest XUL browser (48.7 in Speedometer 2.1 compared to 22.4 in Pale Moon 34.2 AVX2)
|
||||
- Re-add support for iOS, Android, Windows 2000 and later, NetBSD (soon OpenBSD)
|
||||
|
||||
## Interested in running Dactyloidae in Win9x?
|
||||
|
|
|
|||
1
aclocal.m4
vendored
1
aclocal.m4
vendored
|
|
@ -17,6 +17,7 @@ builtin(include, build/autoconf/altoptions.m4)dnl
|
|||
builtin(include, build/autoconf/mozprog.m4)dnl
|
||||
builtin(include, build/autoconf/mozheader.m4)dnl
|
||||
builtin(include, build/autoconf/lto.m4)dnl
|
||||
builtin(include, build/autoconf/clang-cl-pgo.m4)dnl
|
||||
builtin(include, build/autoconf/frameptr.m4)dnl
|
||||
builtin(include, build/autoconf/compiler-opts.m4)dnl
|
||||
builtin(include, build/autoconf/expandlibs.m4)dnl
|
||||
|
|
|
|||
30
build/autoconf/clang-cl-pgo.m4
Normal file
30
build/autoconf/clang-cl-pgo.m4
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
dnl This Source Code Form is subject to the terms of the Mozilla Public
|
||||
dnl License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
AC_DEFUN([MOZ_CLANG_CL_PGO], [
|
||||
if test -n "$CLANG_CL"; then
|
||||
dnl Override the MSVC/GCC flags: clang-cl uses LLVM instrumentation.
|
||||
PROFILE_GEN_CFLAGS="-clang:-fprofile-instr-generate"
|
||||
PROFILE_USE_CFLAGS='-clang:-fprofile-instr-use="$(DEPTH)/merged.profdata"'
|
||||
PROFILE_GEN_LDFLAGS=
|
||||
PROFILE_USE_LDFLAGS=
|
||||
|
||||
if test -n "$MOZ_PGO"; then
|
||||
AC_PATH_PROG(LLVM_PROFDATA, llvm-profdata.exe)
|
||||
if test -z "$LLVM_PROFDATA"; then
|
||||
AC_MSG_ERROR([clang-cl PGO requires llvm-profdata from the compiler's LLVM installation on PATH])
|
||||
fi
|
||||
dnl The training harness uses native Windows Python, not an MSYS shell.
|
||||
LLVM_PROFDATA="$(cd "$(dirname "$LLVM_PROFDATA")" && pwd -W)/$(basename "$LLVM_PROFDATA")"
|
||||
dnl clang-cl emits a default-library directive for its profile runtime.
|
||||
dnl We invoke the linker directly, so supply the runtime search path.
|
||||
clang_resource_dir=`$CC -print-resource-dir | tr '\\' '/'`
|
||||
if test ! -d "$clang_resource_dir/lib/windows"; then
|
||||
AC_MSG_ERROR([clang-cl PGO requires the Windows compiler-rt profile runtime])
|
||||
fi
|
||||
PROFILE_GEN_LDFLAGS="-LIBPATH:\"$clang_resource_dir/lib/windows\""
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(LLVM_PROFDATA)
|
||||
])
|
||||
|
|
@ -84,7 +84,14 @@ def get_sdk_dirs(sdk, subdir):
|
|||
include_dirs['winv6.3'] = include_dirs['include']
|
||||
del include_dirs['include']
|
||||
|
||||
valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True)
|
||||
def build_num(v):
|
||||
parts = v.split('.')
|
||||
try:
|
||||
return int(parts[2]) if len(parts) >= 3 else 0
|
||||
except ValueError:
|
||||
return 0
|
||||
valid_versions = sorted((v for v in set(include_dirs) & set(lib_dirs)
|
||||
if build_num(v) <= 19041), reverse=True)
|
||||
if valid_versions:
|
||||
return namespace(
|
||||
path=sdk,
|
||||
|
|
@ -136,6 +143,13 @@ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
|
|||
'WINDOWSSDKDIR (%s). Please verify it contains a valid and '
|
||||
'complete SDK installation.' % windows_sdk_dir_env)
|
||||
|
||||
def sdk_build(path):
|
||||
parts = os.path.basename(path).split('.')
|
||||
try:
|
||||
return int(parts[2]) if len(parts) >= 3 else 0
|
||||
except ValueError:
|
||||
return 0
|
||||
sdks = {k: v for k, v in sdks.items() if sdk_build(k) <= 19041}
|
||||
valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
|
||||
if valid_sdks:
|
||||
biggest_version, sdk = sdks[valid_sdks[0]]
|
||||
|
|
@ -226,6 +240,7 @@ def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env):
|
|||
'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal '
|
||||
'CRT.' % windows_sdk_dir_env)
|
||||
|
||||
sdks = {k: v for k, v in sdks.items() if int(str(v[0]).split('.')[2]) <= 19041}
|
||||
valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
|
||||
if not valid_sdks:
|
||||
raise FatalCheckError('Cannot find the Universal CRT SDK. '
|
||||
|
|
|
|||
43
build/pgo/llvm_pgo.py
Normal file
43
build/pgo/llvm_pgo.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
"""Collect LLVM profiles outside the directories cleaned between PGO passes."""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
class LLVMProfile(object):
|
||||
def __init__(self, topobjdir, profdata):
|
||||
if not profdata or not os.path.isfile(profdata):
|
||||
raise RuntimeError("clang-cl PGO requires a configured llvm-profdata")
|
||||
self.topobjdir = os.path.abspath(topobjdir)
|
||||
self.profdata = profdata
|
||||
self.output = os.path.join(self.topobjdir, 'merged.profdata')
|
||||
self.directory = None
|
||||
|
||||
def prepare(self, env):
|
||||
# Never reuse a previous training run's merged or raw profiles.
|
||||
if os.path.exists(self.output):
|
||||
os.remove(self.output)
|
||||
self.directory = tempfile.mkdtemp(prefix='pgo-profiles-', dir=self.topobjdir)
|
||||
# %m distinguishes instrumented DLLs; %p distinguishes child processes.
|
||||
env['LLVM_PROFILE_FILE'] = os.path.join(self.directory, '%m-%p.profraw')
|
||||
|
||||
def merge(self):
|
||||
profiles = sorted(glob.glob(os.path.join(self.directory, '*.profraw')))
|
||||
if not profiles or not any(os.path.getsize(p) for p in profiles):
|
||||
raise RuntimeError("PGO training produced no LLVM profile data")
|
||||
# An input list avoids Windows command-line length limits. Keep profiles
|
||||
# on failure so that corrupt or incompatible data can be diagnosed.
|
||||
inputs = os.path.join(self.directory, 'profiles.list')
|
||||
with open(inputs, 'w') as stream:
|
||||
for profile in profiles:
|
||||
stream.write(profile + '\n')
|
||||
subprocess.check_call([self.profdata, 'merge', '-o', self.output,
|
||||
'-f', inputs])
|
||||
if not os.path.isfile(self.output) or not os.path.getsize(self.output):
|
||||
raise RuntimeError("llvm-profdata did not produce a merged profile")
|
||||
|
|
@ -18,9 +18,20 @@ import tempfile
|
|||
from datetime import datetime
|
||||
from mozbuild.base import MozbuildObject
|
||||
from buildconfig import substs
|
||||
from llvm_pgo import LLVMProfile
|
||||
|
||||
PORT = 8888
|
||||
|
||||
|
||||
def wait_for_training(runner, timeout):
|
||||
result = runner.wait(timeout=timeout)
|
||||
if result is None:
|
||||
runner.stop()
|
||||
raise RuntimeError("PGO training browser timed out after %s seconds" % timeout)
|
||||
if result != 0:
|
||||
raise RuntimeError("PGO training browser exited unsuccessfully")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli = CLI()
|
||||
debug_args, interactive = cli.debugger_arguments()
|
||||
|
|
@ -54,6 +65,15 @@ if __name__ == '__main__':
|
|||
|
||||
env = os.environ.copy()
|
||||
env["XPCOM_DEBUG_BREAK"] = "warn"
|
||||
# Keep the training workload in the parent process. In particular, the
|
||||
# initialization javascript: URL must work without a content subprocess.
|
||||
# This environment belongs only to the temporary training browser.
|
||||
env["MOZ_FORCE_DISABLE_E10S"] = "1"
|
||||
|
||||
llvm_profile = None
|
||||
if substs.get("CLANG_CL") and substs.get("MOZ_PGO"):
|
||||
llvm_profile = LLVMProfile(build.topobjdir, substs.get("LLVM_PROFDATA"))
|
||||
llvm_profile.prepare(env)
|
||||
|
||||
# For VC12+, make sure we can find the right bitness of pgort1x0.dll
|
||||
if not substs.get('HAVE_64BIT_BUILD'):
|
||||
|
|
@ -72,7 +92,7 @@ if __name__ == '__main__':
|
|||
cmdargs=['javascript:Quitter.quit()'],
|
||||
env=env)
|
||||
runner.start()
|
||||
runner.wait()
|
||||
wait_for_training(runner, 180)
|
||||
|
||||
jarlog = os.getenv("JARLOG_FILE")
|
||||
if jarlog:
|
||||
|
|
@ -85,7 +105,9 @@ if __name__ == '__main__':
|
|||
cmdargs=cmdargs,
|
||||
env=env)
|
||||
runner.start(debug_args=debug_args, interactive=interactive)
|
||||
runner.wait()
|
||||
httpd.stop()
|
||||
wait_for_training(runner, 600)
|
||||
if llvm_profile:
|
||||
llvm_profile.merge()
|
||||
finally:
|
||||
httpd.stop()
|
||||
shutil.rmtree(profilePath)
|
||||
|
|
|
|||
139
build/pgo/test_llvm_pgo.py
Normal file
139
build/pgo/test_llvm_pgo.py
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from distutils.spawn import find_executable
|
||||
|
||||
from llvm_pgo import LLVMProfile
|
||||
|
||||
|
||||
class LLVMProfileTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.directory = tempfile.mkdtemp(prefix='llvm pgo test ')
|
||||
self.profile = LLVMProfile(self.directory, sys.executable)
|
||||
self.check_call = subprocess.check_call
|
||||
|
||||
def tearDown(self):
|
||||
subprocess.check_call = self.check_call
|
||||
shutil.rmtree(self.directory)
|
||||
|
||||
def write(self, path, data):
|
||||
with open(path, 'w') as stream:
|
||||
stream.write(data)
|
||||
|
||||
def test_missing_tool(self):
|
||||
with self.assertRaises(RuntimeError):
|
||||
LLVMProfile(self.directory, None)
|
||||
|
||||
def test_prepare_isolates_runs(self):
|
||||
self.write(self.profile.output, 'stale merged profile')
|
||||
env = {}
|
||||
self.profile.prepare(env)
|
||||
first = self.profile.directory
|
||||
self.write(os.path.join(first, 'stale.profraw'), 'stale raw profile')
|
||||
self.profile.prepare(env)
|
||||
self.assertNotEqual(first, self.profile.directory)
|
||||
self.assertFalse(os.path.exists(self.profile.output))
|
||||
self.assertEqual(env['LLVM_PROFILE_FILE'],
|
||||
os.path.join(self.profile.directory, '%m-%p.profraw'))
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.profile.merge()
|
||||
|
||||
def test_empty_profiles(self):
|
||||
self.profile.prepare({})
|
||||
self.write(os.path.join(self.profile.directory, 'empty.profraw'), '')
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.profile.merge()
|
||||
|
||||
def test_merge_uses_input_list(self):
|
||||
self.profile.prepare({})
|
||||
raw = os.path.join(self.profile.directory, 'module-process.profraw')
|
||||
self.write(raw, 'raw profile')
|
||||
def merge(args):
|
||||
self.assertEqual(args[:4], [sys.executable, 'merge', '-o',
|
||||
self.profile.output])
|
||||
self.assertEqual(args[4], '-f')
|
||||
with open(args[5]) as stream:
|
||||
self.assertEqual(stream.read().splitlines(), [raw])
|
||||
self.write(self.profile.output, 'merged profile')
|
||||
subprocess.check_call = merge
|
||||
self.profile.merge()
|
||||
|
||||
def test_merge_failure_propagates(self):
|
||||
self.profile.prepare({})
|
||||
self.write(os.path.join(self.profile.directory, 'bad.profraw'), 'bad')
|
||||
def fail(args):
|
||||
raise subprocess.CalledProcessError(1, args)
|
||||
subprocess.check_call = fail
|
||||
with self.assertRaises(subprocess.CalledProcessError):
|
||||
self.profile.merge()
|
||||
|
||||
def test_merge_requires_output(self):
|
||||
self.profile.prepare({})
|
||||
self.write(os.path.join(self.profile.directory, 'data.profraw'), 'raw')
|
||||
subprocess.check_call = lambda args: None
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.profile.merge()
|
||||
|
||||
|
||||
class PGOConfigurationTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.make = (os.environ.get('MAKE') or find_executable('mozmake') or
|
||||
find_executable('make'))
|
||||
if not self.make:
|
||||
self.skipTest('GNU make is required')
|
||||
self.directory = tempfile.mkdtemp(prefix='pgo configuration ')
|
||||
config = os.path.join(os.path.dirname(__file__), '..', '..',
|
||||
'config', 'config.mk')
|
||||
with open(config) as stream:
|
||||
source = stream.read()
|
||||
start = source.index('# Reject stale Windows PGO configuration')
|
||||
end = source.index('# Enable profile-based feedback', start)
|
||||
self.makefile = os.path.join(self.directory, 'Makefile')
|
||||
with open(self.makefile, 'w') as stream:
|
||||
stream.write(source[start:end] + '\nall:;\n')
|
||||
|
||||
def tearDown(self):
|
||||
if hasattr(self, 'directory'):
|
||||
shutil.rmtree(self.directory)
|
||||
|
||||
def run_make(self, *variables):
|
||||
process = subprocess.Popen([self.make, '-f', self.makefile] +
|
||||
list(variables), stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
output = process.communicate()[0].decode('utf-8')
|
||||
return process.returncode, output
|
||||
|
||||
def test_missing_profdata_fails_before_build(self):
|
||||
code, output = self.run_make('MOZ_PGO=1', 'CLANG_CL=1', 'LLVM_PROFDATA=')
|
||||
self.assertNotEqual(code, 0)
|
||||
self.assertIn('requires LLVM_PROFDATA', output)
|
||||
|
||||
def test_msvc_flags_rejected_for_clang(self):
|
||||
code, output = self.run_make('MOZ_PGO=1', 'CLANG_CL=1',
|
||||
'LLVM_PROFDATA=llvm-profdata.exe',
|
||||
'PROFILE_GEN_CFLAGS=-GL')
|
||||
self.assertNotEqual(code, 0)
|
||||
self.assertIn('requires LLVM instrumentation flags', output)
|
||||
|
||||
def test_llvm_configuration_accepted(self):
|
||||
code, output = self.run_make('MOZ_PGO=1', 'CLANG_CL=1',
|
||||
'LLVM_PROFDATA=llvm-profdata.exe',
|
||||
'PROFILE_GEN_CFLAGS=-clang:-fprofile-instr-generate')
|
||||
self.assertEqual(code, 0, output)
|
||||
|
||||
def test_non_pgo_and_msvc_unaffected(self):
|
||||
for variables in [('MOZ_PGO=', 'CLANG_CL=1'),
|
||||
('MOZ_PGO=1', 'CLANG_CL=')]:
|
||||
code, output = self.run_make(*variables)
|
||||
self.assertEqual(code, 0, output)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -210,13 +210,25 @@ ifdef CPP_UNIT_TESTS
|
|||
NO_PROFILE_GUIDED_OPTIMIZE = 1
|
||||
endif
|
||||
|
||||
# Reject stale Windows PGO configuration before compiling the first pass.
|
||||
ifdef MOZ_PGO
|
||||
ifdef CLANG_CL
|
||||
ifeq ($(strip $(LLVM_PROFDATA)),)
|
||||
$(error clang-cl PGO requires LLVM_PROFDATA; rerun ./mach configure)
|
||||
endif
|
||||
ifeq ($(filter -clang:-fprofile-instr-generate,$(PROFILE_GEN_CFLAGS)),)
|
||||
$(error clang-cl PGO requires LLVM instrumentation flags; rerun ./mach configure)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# Enable profile-based feedback
|
||||
ifneq (1,$(NO_PROFILE_GUIDED_OPTIMIZE))
|
||||
ifdef MOZ_PROFILE_GENERATE
|
||||
OS_CFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_GEN_CFLAGS))
|
||||
OS_CXXFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_GEN_CFLAGS))
|
||||
OS_LDFLAGS += $(PROFILE_GEN_LDFLAGS)
|
||||
ifeq (WINNT,$(OS_ARCH))
|
||||
ifeq ($(OS_ARCH)_$(CLANG_CL),WINNT_)
|
||||
AR_FLAGS += -LTCG
|
||||
endif
|
||||
endif # MOZ_PROFILE_GENERATE
|
||||
|
|
@ -225,7 +237,7 @@ ifdef MOZ_PROFILE_USE
|
|||
OS_CFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_USE_CFLAGS))
|
||||
OS_CXXFLAGS += $(if $(filter $(notdir $<),$(notdir $(NO_PROFILE_GUIDED_OPTIMIZE))),,$(PROFILE_USE_CFLAGS))
|
||||
OS_LDFLAGS += $(PROFILE_USE_LDFLAGS)
|
||||
ifeq (WINNT,$(OS_ARCH))
|
||||
ifeq ($(OS_ARCH)_$(CLANG_CL),WINNT_)
|
||||
AR_FLAGS += -LTCG
|
||||
endif
|
||||
endif # MOZ_PROFILE_USE
|
||||
|
|
|
|||
|
|
@ -531,7 +531,7 @@ endif
|
|||
##############################################
|
||||
ifneq (1,$(NO_PROFILE_GUIDED_OPTIMIZE))
|
||||
ifdef MOZ_PROFILE_USE
|
||||
ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||
ifeq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__)
|
||||
# When building with PGO, we have to make sure to re-link
|
||||
# in the MOZ_PROFILE_USE phase if we linked in the
|
||||
# MOZ_PROFILE_GENERATE phase. We'll touch this pgo.relink
|
||||
|
|
@ -563,7 +563,7 @@ endif # MOZ_PROFILE_USE
|
|||
ifdef MOZ_PROFILE_GENERATE
|
||||
# Clean up profiling data during PROFILE_GENERATE phase
|
||||
export::
|
||||
ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||
ifeq ($(OS_ARCH)_$(GNU_CC)_$(CLANG_CL), WINNT__)
|
||||
$(foreach pgd,$(wildcard *.pgd),pgomgr -clear $(pgd);)
|
||||
else
|
||||
ifdef GNU_CC
|
||||
|
|
|
|||
|
|
@ -34,11 +34,6 @@
|
|||
#include "FlacDecoder.h"
|
||||
#include "FlacDemuxer.h"
|
||||
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
#include "DirectShowDecoder.h"
|
||||
#include "DirectShowReader.h"
|
||||
#endif
|
||||
|
||||
#include "nsPluginHost.h"
|
||||
#include "MediaPrefs.h"
|
||||
|
||||
|
|
@ -143,14 +138,6 @@ IsFlacSupportedType(const nsACString& aType,
|
|||
return FlacDecoder::CanHandleMediaType(aType, aCodecs);
|
||||
}
|
||||
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
static bool
|
||||
IsDirectShowSupportedType(const nsACString& aType)
|
||||
{
|
||||
return DirectShowDecoder::GetSupportedCodecs(aType, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
static
|
||||
CanPlayStatus
|
||||
CanHandleCodecsType(const MediaContentType& aType,
|
||||
|
|
@ -210,11 +197,6 @@ CanHandleCodecsType(const MediaContentType& aType,
|
|||
if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) {
|
||||
return CANPLAY_YES;
|
||||
}
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
if (IsDirectShowSupportedType(aType.GetMIMEType())) {
|
||||
return CANPLAY_YES;
|
||||
}
|
||||
#endif
|
||||
if (!codecList) {
|
||||
return CANPLAY_MAYBE;
|
||||
}
|
||||
|
|
@ -276,11 +258,6 @@ CanHandleMediaType(const MediaContentType& aType,
|
|||
if (IsFlacSupportedType(aType.GetMIMEType())) {
|
||||
return CANPLAY_MAYBE;
|
||||
}
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
if (IsDirectShowSupportedType(aType.GetMIMEType())) {
|
||||
return CANPLAY_MAYBE;
|
||||
}
|
||||
#endif
|
||||
return CANPLAY_NO;
|
||||
}
|
||||
|
||||
|
|
@ -370,13 +347,6 @@ InstantiateDecoder(const nsACString& aType,
|
|||
return decoder.forget();
|
||||
}
|
||||
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
if (IsDirectShowSupportedType(aType)) {
|
||||
decoder = new DirectShowDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -423,11 +393,6 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
|
|||
decoderReader =
|
||||
new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource()));
|
||||
}
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
else if (IsDirectShowSupportedType(aType)) {
|
||||
decoderReader = new DirectShowReader(aDecoder);
|
||||
}
|
||||
#endif
|
||||
|
||||
return decoderReader;
|
||||
}
|
||||
|
|
@ -453,9 +418,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
|
|||
IsAACSupportedType(aType) ||
|
||||
IsWaveSupportedType(aType) ||
|
||||
IsFlacSupportedType(aType) ||
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
IsDirectShowSupportedType(aType) ||
|
||||
#endif
|
||||
false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,290 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SampleSink.h"
|
||||
#include "AudioSinkFilter.h"
|
||||
#include "AudioSinkInputPin.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
|
||||
#include <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
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(AudioSinkFilter_h_)
|
||||
#define AudioSinkFilter_h_
|
||||
|
||||
#include "BaseFilter.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AudioSinkInputPin;
|
||||
class SampleSink;
|
||||
|
||||
// Filter that acts as the end of the graph. Audio samples input into
|
||||
// this filter block the calling thread, and the calling thread is
|
||||
// unblocked when the decode thread extracts the sample. The samples
|
||||
// input into this filter are stored in the SampleSink, where the blocking
|
||||
// is implemented. The input pin owns the SampleSink.
|
||||
class AudioSinkFilter: public mozilla::media::BaseFilter,
|
||||
public IMediaSeeking
|
||||
{
|
||||
|
||||
public:
|
||||
AudioSinkFilter(const wchar_t* aObjectName, HRESULT* aOutResult);
|
||||
virtual ~AudioSinkFilter();
|
||||
|
||||
// Gets the input pin's sample sink.
|
||||
SampleSink* GetSampleSink();
|
||||
const ::VIDEOINFOHEADER* GetVideoInfo() const;
|
||||
|
||||
// IUnknown implementation.
|
||||
STDMETHODIMP QueryInterface(REFIID aIId, void **aInterface);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// CBaseFilter methods
|
||||
int GetPinCount ();
|
||||
mozilla::media::BasePin* GetPin ( IN int Index);
|
||||
STDMETHODIMP Pause ();
|
||||
STDMETHODIMP Stop ();
|
||||
STDMETHODIMP GetClassID ( OUT CLSID * pCLSID);
|
||||
STDMETHODIMP Run(REFERENCE_TIME tStart);
|
||||
// IMediaSeeking Methods...
|
||||
|
||||
// We defer to SourceFilter, but we must expose the interface on
|
||||
// the output pins. Seeking commands come upstream from the renderers,
|
||||
// but they must be actioned at the source filters.
|
||||
STDMETHODIMP GetCapabilities(DWORD* aCapabilities);
|
||||
STDMETHODIMP CheckCapabilities(DWORD* aCapabilities);
|
||||
STDMETHODIMP IsFormatSupported(const GUID* aFormat);
|
||||
STDMETHODIMP QueryPreferredFormat(GUID* aFormat);
|
||||
STDMETHODIMP GetTimeFormat(GUID* aFormat);
|
||||
STDMETHODIMP IsUsingTimeFormat(const GUID* aFormat);
|
||||
STDMETHODIMP SetTimeFormat(const GUID* aFormat);
|
||||
STDMETHODIMP GetDuration(LONGLONG* pDuration);
|
||||
STDMETHODIMP GetStopPosition(LONGLONG* pStop);
|
||||
STDMETHODIMP GetCurrentPosition(LONGLONG* aCurrent);
|
||||
STDMETHODIMP ConvertTimeFormat(LONGLONG* aTarget,
|
||||
const GUID* aTargetFormat,
|
||||
LONGLONG aSource,
|
||||
const GUID* aSourceFormat);
|
||||
STDMETHODIMP SetPositions(LONGLONG* aCurrent,
|
||||
DWORD aCurrentFlags,
|
||||
LONGLONG* aStop,
|
||||
DWORD aStopFlags);
|
||||
STDMETHODIMP GetPositions(LONGLONG* aCurrent,
|
||||
LONGLONG* aStop);
|
||||
STDMETHODIMP GetAvailable(LONGLONG* aEarliest,
|
||||
LONGLONG* aLatest);
|
||||
STDMETHODIMP SetRate(double aRate);
|
||||
STDMETHODIMP GetRate(double* aRate);
|
||||
STDMETHODIMP GetPreroll(LONGLONG* aPreroll);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// class factory calls this
|
||||
static IUnknown * CreateInstance (IN LPUNKNOWN punk, OUT HRESULT * phr);
|
||||
|
||||
private:
|
||||
CriticalSection mFilterCritSec;
|
||||
|
||||
// Note: The input pin defers its refcounting to the sink filter, so when
|
||||
// the input pin is addrefed, what actually happens is the sink filter is
|
||||
// addrefed.
|
||||
nsAutoPtr<AudioSinkInputPin> mInputPin;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // AudioSinkFilter_h_
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AudioSinkInputPin.h"
|
||||
#include "AudioSinkFilter.h"
|
||||
#include "SampleSink.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
#include <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
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(AudioSinkInputPin_h_)
|
||||
#define AudioSinkInputPin_h_
|
||||
|
||||
#include "BaseInputPin.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace media {
|
||||
class MediaType;
|
||||
}
|
||||
|
||||
class AudioSinkFilter;
|
||||
class SampleSink;
|
||||
|
||||
|
||||
// Input pin for capturing audio output of a DirectShow filter graph.
|
||||
// This is the input pin for the AudioSinkFilter.
|
||||
class AudioSinkInputPin: public mozilla::media::BaseInputPin
|
||||
{
|
||||
public:
|
||||
AudioSinkInputPin(wchar_t* aObjectName,
|
||||
AudioSinkFilter* aFilter,
|
||||
mozilla::CriticalSection* aLock,
|
||||
HRESULT* aOutResult);
|
||||
virtual ~AudioSinkInputPin();
|
||||
|
||||
HRESULT GetMediaType (IN int iPos, OUT mozilla::media::MediaType * pmt);
|
||||
HRESULT CheckMediaType (IN const mozilla::media::MediaType * pmt);
|
||||
STDMETHODIMP Receive (IN IMediaSample *);
|
||||
STDMETHODIMP BeginFlush() override;
|
||||
STDMETHODIMP EndFlush() override;
|
||||
|
||||
// Called when we start decoding a new segment, that happens directly after
|
||||
// a seek. This captures the segment's start time. Samples decoded by the
|
||||
// MP3 decoder have their timestamps offset from the segment start time.
|
||||
// Storing the segment start time enables us to set each sample's MediaTime
|
||||
// as an offset in the stream relative to the start of the stream, rather
|
||||
// than the start of the segment, i.e. its absolute time in the stream.
|
||||
STDMETHODIMP NewSegment(REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate) override;
|
||||
|
||||
STDMETHODIMP EndOfStream() override;
|
||||
|
||||
// Returns the IMediaSeeking interface of the connected output pin.
|
||||
// We forward seeking requests upstream from the sink to the source
|
||||
// filters.
|
||||
already_AddRefed<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_
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DirectShowDecoder.h"
|
||||
#include "DirectShowReader.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MediaDecoderStateMachine* DirectShowDecoder::CreateStateMachine()
|
||||
{
|
||||
return new MediaDecoderStateMachine(this, new DirectShowReader(this));
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
DirectShowDecoder::GetSupportedCodecs(const nsACString& aType,
|
||||
char const *const ** aCodecList)
|
||||
{
|
||||
if (!IsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static char const *const mp3AudioCodecs[] = {
|
||||
"mp3",
|
||||
nullptr
|
||||
};
|
||||
if (aType.EqualsASCII("audio/mpeg") ||
|
||||
aType.EqualsASCII("audio/mp3")) {
|
||||
if (aCodecList) {
|
||||
*aCodecList = mp3AudioCodecs;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static char const *const h264VideoCodecs[] = {
|
||||
"avc1",
|
||||
"h264",
|
||||
nullptr
|
||||
};
|
||||
if ((aType.EqualsASCII("video/h264") ||
|
||||
aType.EqualsASCII("video/avc")) &&
|
||||
CanDecodeH264UsingDirectShow()) {
|
||||
if (aCodecList) {
|
||||
*aCodecList = h264VideoCodecs;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
DirectShowDecoder::IsEnabled()
|
||||
{
|
||||
return Preferences::GetBool("media.directshow.enabled") &&
|
||||
(CanDecodeMP3UsingDirectShow() || CanDecodeH264UsingDirectShow());
|
||||
}
|
||||
|
||||
DirectShowDecoder::DirectShowDecoder(MediaDecoderOwner* aOwner)
|
||||
: MediaDecoder(aOwner)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DirectShowDecoder);
|
||||
}
|
||||
|
||||
DirectShowDecoder::~DirectShowDecoder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DirectShowDecoder);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(DirectShowDecoder_h_)
|
||||
#define DirectShowDecoder_h_
|
||||
|
||||
#include "MediaDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Decoder that uses the legacy DirectShow graph on systems without WMF.
|
||||
class DirectShowDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
|
||||
explicit DirectShowDecoder(MediaDecoderOwner* aOwner);
|
||||
virtual ~DirectShowDecoder();
|
||||
|
||||
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
|
||||
if (!IsEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new DirectShowDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
|
||||
// Returns true if aType is a MIME type that we render with the
|
||||
// DirectShow backend. If aCodecList is non null,
|
||||
// it is filled with a (static const) null-terminated list of strings
|
||||
// denoting the codecs we'll playback. Note that playback is strictly
|
||||
// limited to MP3 only.
|
||||
static bool GetSupportedCodecs(const nsACString& aType,
|
||||
char const *const ** aCodecList);
|
||||
|
||||
// Returns true if the DirectShow backend is preffed on.
|
||||
static bool IsEnabled();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
@ -1,417 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DirectShowReader.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "AudioSinkFilter.h"
|
||||
#include "SourceFilter.h"
|
||||
#include "SampleSink.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
using namespace mozilla::media;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Windows XP's MP3 decoder filter. This is available on XP only, on Vista
|
||||
// and later we can use the DMO Wrapper filter and MP3 decoder DMO.
|
||||
const GUID DirectShowReader::CLSID_MPEG_LAYER_3_DECODER_FILTER =
|
||||
{ 0x38BE3000, 0xDBF4, 0x11D0, {0x86, 0x0E, 0x00, 0xA0, 0x24, 0xCF, 0xEF, 0x6D} };
|
||||
|
||||
|
||||
static LazyLogModule gDirectShowLog("DirectShowDecoder");
|
||||
#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaDecoderReader(aDecoder),
|
||||
#ifdef DIRECTSHOW_REGISTER_GRAPH
|
||||
mRotRegister(0),
|
||||
#endif
|
||||
mNumChannels(0),
|
||||
mAudioRate(0),
|
||||
mBytesPerSample(0),
|
||||
mIsH264(aDecoder->GetResource()->GetContentType().EqualsLiteral("video/h264") ||
|
||||
aDecoder->GetResource()->GetContentType().EqualsLiteral("video/avc"))
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_CTOR(DirectShowReader);
|
||||
if (mIsH264) {
|
||||
Preferences::SetBool("media.directshow.h264.active", true);
|
||||
}
|
||||
}
|
||||
|
||||
DirectShowReader::~DirectShowReader()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_DTOR(DirectShowReader);
|
||||
if (mIsH264) {
|
||||
Preferences::SetBool("media.directshow.h264.active", false);
|
||||
}
|
||||
#ifdef DIRECTSHOW_REGISTER_GRAPH
|
||||
if (mRotRegister) {
|
||||
RemoveGraphFromRunningObjectTable(mRotRegister);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
DirectShowReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
HRESULT hr;
|
||||
nsresult rv;
|
||||
|
||||
// Create the filter graph, reference it by the GraphBuilder interface,
|
||||
// to make graph building more convenient.
|
||||
hr = CoCreateInstance(CLSID_FilterGraph,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IGraphBuilder,
|
||||
reinterpret_cast<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
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(DirectShowReader_h_)
|
||||
#define DirectShowReader_h_
|
||||
|
||||
#include "windows.h" // HRESULT, DWORD
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "MediaResource.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
// Add the graph to the Running Object Table so that we can connect
|
||||
// to this graph with GraphEdit/GraphStudio. Note: you must
|
||||
// also regsvr32 proppage.dll from the Windows SDK.
|
||||
// See: http://msdn.microsoft.com/en-us/library/ms787252(VS.85).aspx
|
||||
// #define DIRECTSHOW_REGISTER_GRAPH
|
||||
|
||||
struct IGraphBuilder;
|
||||
struct IMediaControl;
|
||||
struct IMediaSeeking;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AudioSinkFilter;
|
||||
class SourceFilter;
|
||||
|
||||
// Decoder backend for decoding legacy media using DirectShow. DirectShow operates as
|
||||
// a filter graph. The basic design of the DirectShowReader is that we have
|
||||
// a SourceFilter that wraps the MediaResource that connects to the
|
||||
// MP3 decoder filter. The MP3 decoder filter "pulls" data as it requires it
|
||||
// downstream on its own thread. When the MP3 decoder has produced a block of
|
||||
// decoded samples, its thread calls downstream into our AudioSinkFilter,
|
||||
// passing the decoded buffer in. The AudioSinkFilter inserts the samples into
|
||||
// a SampleSink object. The SampleSink blocks the MP3 decoder's thread until
|
||||
// the decode thread calls DecodeAudioData(), whereupon the SampleSink
|
||||
// releases the decoded samples to the decode thread, and unblocks the MP3
|
||||
// decoder's thread. The MP3 decoder can then request more data from the
|
||||
// SourceFilter, and decode more data. If the decode thread calls
|
||||
// DecodeAudioData() and there's no decoded samples waiting to be extracted
|
||||
// in the SampleSink, the SampleSink blocks the decode thread until the MP3
|
||||
// decoder produces a decoded sample.
|
||||
class DirectShowReader : public MediaDecoderReader
|
||||
{
|
||||
public:
|
||||
DirectShowReader(AbstractMediaDecoder* aDecoder);
|
||||
|
||||
virtual ~DirectShowReader();
|
||||
|
||||
bool DecodeAudioData() override;
|
||||
bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold) override;
|
||||
|
||||
nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) override;
|
||||
|
||||
RefPtr<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
|
||||
|
|
@ -1,457 +0,0 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DirectShowUtils.h"
|
||||
#include "DirectShowReader.h"
|
||||
#include "dmodshow.h"
|
||||
#include "wmcodecdsp.h"
|
||||
#include "dmoreg.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include <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
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _DirectShowUtils_h_
|
||||
#define _DirectShowUtils_h_
|
||||
|
||||
#include <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
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SampleSink.h"
|
||||
#include "AudioSinkFilter.h"
|
||||
#include "AudioSinkInputPin.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
using namespace mozilla::media;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static LazyLogModule gDirectShowLog("DirectShowDecoder");
|
||||
#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
SampleSink::SampleSink()
|
||||
: mMonitor("SampleSink"),
|
||||
mIsFlushing(false),
|
||||
mAtEOS(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(SampleSink);
|
||||
}
|
||||
|
||||
SampleSink::~SampleSink()
|
||||
{
|
||||
MOZ_COUNT_DTOR(SampleSink);
|
||||
}
|
||||
|
||||
void
|
||||
SampleSink::SetAudioFormat(const WAVEFORMATEX* aInFormat)
|
||||
{
|
||||
NS_ENSURE_TRUE(aInFormat, );
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
memcpy(&mAudioFormat, aInFormat, sizeof(WAVEFORMATEX));
|
||||
}
|
||||
|
||||
void
|
||||
SampleSink::GetAudioFormat(WAVEFORMATEX* aOutFormat)
|
||||
{
|
||||
MOZ_ASSERT(aOutFormat);
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
memcpy(aOutFormat, &mAudioFormat, sizeof(WAVEFORMATEX));
|
||||
}
|
||||
|
||||
HRESULT
|
||||
SampleSink::Receive(IMediaSample* aSample)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
|
||||
while (true) {
|
||||
if (mIsFlushing) {
|
||||
return S_FALSE;
|
||||
}
|
||||
if (!mSample) {
|
||||
break;
|
||||
}
|
||||
if (mAtEOS) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
// Wait until the consumer thread consumes the sample.
|
||||
mon.Wait();
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(gDirectShowLog, LogLevel::Debug)) {
|
||||
REFERENCE_TIME start = 0, end = 0;
|
||||
HRESULT hr = aSample->GetMediaTime(&start, &end);
|
||||
LOG("SampleSink::Receive() [%4.2lf-%4.2lf]",
|
||||
(double)RefTimeToUsecs(start) / USECS_PER_S,
|
||||
(double)RefTimeToUsecs(end) / USECS_PER_S);
|
||||
}
|
||||
|
||||
mSample = aSample;
|
||||
// Notify the signal, to awaken the consumer thread in WaitForSample()
|
||||
// if necessary.
|
||||
mon.NotifyAll();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
SampleSink::Extract(RefPtr<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
|
||||
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(SampleSink_h_)
|
||||
#define SampleSink_h_
|
||||
|
||||
#include "BaseFilter.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class SampleSink {
|
||||
public:
|
||||
SampleSink();
|
||||
virtual ~SampleSink();
|
||||
|
||||
// Sets the audio format of the incoming samples. The upstream filter
|
||||
// calls this. This makes a copy.
|
||||
void SetAudioFormat(const WAVEFORMATEX* aInFormat);
|
||||
|
||||
// Copies the format of incoming audio samples into into *aOutFormat.
|
||||
void GetAudioFormat(WAVEFORMATEX* aOutFormat);
|
||||
|
||||
// Called when a sample is delivered by the DirectShow graph to the sink.
|
||||
// The decode thread retrieves the sample by calling WaitForSample().
|
||||
// Blocks if there's already a sample waiting to be consumed by the decode
|
||||
// thread.
|
||||
HRESULT Receive(IMediaSample* aSample);
|
||||
|
||||
// Retrieves a sample from the sample queue, blocking until one becomes
|
||||
// available, or until an error occurs. Returns S_FALSE on EOS.
|
||||
HRESULT Extract(RefPtr<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_
|
||||
|
|
@ -1,682 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SourceFilter.h"
|
||||
#include "MediaResource.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include <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
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(nsDirectShowSource_h___)
|
||||
#define nsDirectShowSource_h___
|
||||
|
||||
#include "BaseFilter.h"
|
||||
#include "BasePin.h"
|
||||
#include "MediaType.h"
|
||||
|
||||
#include "nsDeque.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaResource;
|
||||
class OutputPin;
|
||||
|
||||
|
||||
// SourceFilter is an asynchronous DirectShow source filter which
|
||||
// reads from an MediaResource, and supplies data via a pull model downstream
|
||||
// using OutputPin. It us used to supply a generic byte stream into
|
||||
// DirectShow.
|
||||
//
|
||||
// Implements:
|
||||
// * IBaseFilter
|
||||
// * IMediaFilter
|
||||
// * IPersist
|
||||
// * IUnknown
|
||||
//
|
||||
class DECLSPEC_UUID("5c2a7ad0-ba82-4659-9178-c4719a2765d6")
|
||||
SourceFilter : public media::BaseFilter
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructs source filter to deliver given media type.
|
||||
SourceFilter(const GUID& aMajorType, const GUID& aSubType);
|
||||
~SourceFilter();
|
||||
|
||||
nsresult Init(MediaResource *aResource, int64_t aMP3Offset);
|
||||
|
||||
// BaseFilter overrides.
|
||||
// Only one output - the byte stream.
|
||||
int GetPinCount() override { return 1; }
|
||||
|
||||
media::BasePin* GetPin(int n) override;
|
||||
|
||||
// Get's the media type we're supplying.
|
||||
const media::MediaType* GetMediaType() const;
|
||||
|
||||
uint32_t GetAndResetBytesConsumedCount();
|
||||
|
||||
protected:
|
||||
|
||||
// Our async pull output pin.
|
||||
nsAutoPtr<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
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'AudioSinkFilter.h',
|
||||
'AudioSinkInputPin.h',
|
||||
'DirectShowDecoder.h',
|
||||
'DirectShowReader.h',
|
||||
'DirectShowUtils.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'DirectShowDecoder.cpp',
|
||||
'DirectShowUtils.cpp',
|
||||
'SourceFilter.cpp',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'AudioSinkFilter.cpp',
|
||||
'AudioSinkInputPin.cpp',
|
||||
'DirectShowReader.cpp',
|
||||
'SampleSink.cpp',
|
||||
]
|
||||
|
||||
# If WebRTC isn't being built, we need to compile the DirectShow base classes so that
|
||||
# they're available at link time.
|
||||
if not CONFIG['MOZ_WEBRTC']:
|
||||
SOURCES += [
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseFilter.cpp',
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseInputPin.cpp',
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows/BasePin.cpp',
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows/MediaType.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/media',
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows',
|
||||
]
|
||||
|
|
@ -42,12 +42,6 @@ DIRS += [
|
|||
'standalone',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_DIRECTSHOW']:
|
||||
DIRS += ['directshow']
|
||||
LOCAL_INCLUDES += [
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_FMP4']:
|
||||
DIRS += ['fmp4']
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "TextureD3D11.h"
|
||||
#include "CompositorD3D11Shaders.h"
|
||||
#include "GpuRasterD3D11.h"
|
||||
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#include "nsIWidget.h"
|
||||
|
|
@ -143,6 +144,7 @@ private:
|
|||
|
||||
CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
|
||||
: Compositor(aWidget, aParent)
|
||||
, mGpuRasterViewport(false)
|
||||
, mAttachments(nullptr)
|
||||
, mHwnd(nullptr)
|
||||
, mDisableSequenceForNextFrame(false)
|
||||
|
|
@ -199,6 +201,16 @@ CompositorD3D11::Initialize(nsCString* const out_failureReason)
|
|||
|
||||
mFeatureLevel = mDevice->GetFeatureLevel();
|
||||
|
||||
D3D11_FEATURE_DATA_D3D11_OPTIONS options = {};
|
||||
if (gfxPrefs::D3D11GpuRectangleFills() &&
|
||||
mFeatureLevel >= D3D_FEATURE_LEVEL_10_0 &&
|
||||
SUCCEEDED(mDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS,
|
||||
&options, sizeof(options))) &&
|
||||
options.ClearView) {
|
||||
mContext->QueryInterface(__uuidof(ID3D11DeviceContext1),
|
||||
getter_AddRefs(mGpuRasterContext));
|
||||
}
|
||||
|
||||
mHwnd = mWidget->AsWindows()->GetHwnd();
|
||||
|
||||
memset(&mVSConstants, 0, sizeof(VertexShaderConstants));
|
||||
|
|
@ -656,6 +668,20 @@ CompositorD3D11::GetPSForEffect(Effect* aEffect, MaskType aMaskType)
|
|||
void
|
||||
CompositorD3D11::ClearRect(const gfx::Rect& aRect)
|
||||
{
|
||||
if (mGpuRasterContext && mGpuRasterViewport && mCurrentRT &&
|
||||
!mCurrentRT->HasComplexProjection()) {
|
||||
GpuRasterRect rect = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
|
||||
IntSize size = mCurrentRT->GetSize();
|
||||
D3D11_RECT clip = { 0, 0, size.width, size.height };
|
||||
const float clear[4] = { 0, 0, 0, 0 };
|
||||
if (GpuRasterD3D11FillRect(mGpuRasterContext, mCurrentRT->mRTView,
|
||||
size.width, size.height, &rect, &clip, clear)) {
|
||||
// BeginFrame relies on ClearRect to establish premultiplied blending.
|
||||
mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mContext->OMSetBlendState(mAttachments->mDisabledBlendState, sBlendFactor, 0xFFFFFFFF);
|
||||
|
||||
Matrix4x4 identity;
|
||||
|
|
@ -736,6 +762,39 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
|
||||
MOZ_ASSERT(mCurrentRT, "No render target");
|
||||
|
||||
// Opaque, integer-aligned translated rectangles can be filled directly by
|
||||
// the GPU without shader binding or per-quad constant-buffer uploads.
|
||||
if (mGpuRasterContext && mGpuRasterViewport && !mCurrentRT->HasComplexProjection() &&
|
||||
aEffectChain.mPrimaryEffect->mType == EffectTypes::SOLID_COLOR &&
|
||||
!aEffectChain.mSecondaryEffects[EffectTypes::MASK] &&
|
||||
!aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] &&
|
||||
aOpacity == 1.0f && aTransform.Is2D() && aTransform.As2D().IsTranslation()) {
|
||||
const Color& color = static_cast<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);
|
||||
IntPoint origin = mCurrentRT->GetOrigin();
|
||||
mVSConstants.renderTargetOffset[0] = origin.x;
|
||||
|
|
@ -1193,6 +1252,7 @@ CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize)
|
|||
projection._33 = 0.0f;
|
||||
|
||||
PrepareViewport(aSize, projection, 0.0f, 1.0f);
|
||||
mGpuRasterViewport = true;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1213,6 +1273,7 @@ CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize,
|
|||
const gfx::Matrix4x4& aProjection,
|
||||
float aZNear, float aZFar)
|
||||
{
|
||||
mGpuRasterViewport = false;
|
||||
D3D11_VIEWPORT viewport;
|
||||
viewport.MaxDepth = aZFar;
|
||||
viewport.MinDepth = aZNear;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/layers/Compositor.h"
|
||||
#include "TextureD3D11.h"
|
||||
#include <d3d11.h>
|
||||
#include <d3d11_1.h>
|
||||
|
||||
class nsWidget;
|
||||
|
||||
|
|
@ -174,6 +175,8 @@ private:
|
|||
RefPtr<ID3D11ShaderResourceView>* aOutView);
|
||||
|
||||
RefPtr<ID3D11DeviceContext> mContext;
|
||||
RefPtr<ID3D11DeviceContext1> mGpuRasterContext;
|
||||
bool mGpuRasterViewport;
|
||||
RefPtr<ID3D11Device> mDevice;
|
||||
RefPtr<IDXGISwapChain> mSwapChain;
|
||||
RefPtr<CompositingRenderTargetD3D11> mDefaultRT;
|
||||
|
|
|
|||
84
gfx/layers/d3d11/GpuRasterD3D11.c
Normal file
84
gfx/layers/d3d11/GpuRasterD3D11.c
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#define COBJMACROS
|
||||
#include "GpuRasterD3D11.h"
|
||||
|
||||
#include <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;
|
||||
}
|
||||
36
gfx/layers/d3d11/GpuRasterD3D11.h
Normal file
36
gfx/layers/d3d11/GpuRasterD3D11.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GFX_GPU_RASTER_D3D11_H
|
||||
#define GFX_GPU_RASTER_D3D11_H
|
||||
|
||||
#include <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,6 +80,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
|||
]
|
||||
SOURCES += [
|
||||
'd3d11/CompositorD3D11.cpp',
|
||||
'd3d11/GpuRasterD3D11.c',
|
||||
'd3d11/ReadbackManagerD3D11.cpp',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
|
|
|
|||
148
gfx/tests/gtest/TestGpuRasterD3D11.cpp
Normal file
148
gfx/tests/gtest/TestGpuRasterD3D11.cpp
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
#include "d3d11/GpuRasterD3D11.h"
|
||||
|
||||
#include <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,6 +53,9 @@ LOCAL_INCLUDES += [
|
|||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows' and CONFIG['MOZ_ENABLE_D3D10_LAYER']:
|
||||
SOURCES += ['TestGpuRasterD3D11.cpp']
|
||||
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
|
|
|
|||
|
|
@ -468,6 +468,7 @@ private:
|
|||
DECL_GFX_PREF(Once, "layers.componentalpha.enabled", ComponentAlphaEnabled, bool, true);
|
||||
DECL_GFX_PREF(Live, "layers.composer2d.enabled", Composer2DCompositionEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "layers.d3d11.force-warp", LayersD3D11ForceWARP, bool, false);
|
||||
DECL_GFX_PREF(Once, "layers.d3d11.gpu-rectangle-fills.enabled", D3D11GpuRectangleFills, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.deaa.enabled", LayersDEAAEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.draw-bigimage-borders", DrawBigImageBorders, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.draw-borders", DrawLayerBorders, bool, false);
|
||||
|
|
|
|||
1
js/src/aclocal.m4
vendored
1
js/src/aclocal.m4
vendored
|
|
@ -16,6 +16,7 @@ builtin(include, ../../build/autoconf/altoptions.m4)dnl
|
|||
builtin(include, ../../build/autoconf/mozprog.m4)dnl
|
||||
builtin(include, ../../build/autoconf/mozheader.m4)dnl
|
||||
builtin(include, ../../build/autoconf/lto.m4)dnl
|
||||
builtin(include, ../../build/autoconf/clang-cl-pgo.m4)dnl
|
||||
builtin(include, ../../build/autoconf/frameptr.m4)dnl
|
||||
builtin(include, ../../build/autoconf/compiler-opts.m4)dnl
|
||||
builtin(include, ../../build/autoconf/expandlibs.m4)dnl
|
||||
|
|
|
|||
21
js/src/jit-test/tests/latin1/sse2-search.js
Normal file
21
js/src/jit-test/tests/latin1/sse2-search.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Exercise vector-sized spans, scalar tails, and mixed character encodings.
|
||||
for (var length of [0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65]) {
|
||||
for (var needle of ["\0", "\x7f", "\x80", "\xff", "\u0100", "\ud800", "\uffff"]) {
|
||||
for (var position = 0; position <= length; ++position) {
|
||||
var text = "a".repeat(position) + needle + "a".repeat(length - position);
|
||||
for (var start of [0, position, position + 1, text.length]) {
|
||||
var expected = start <= position ? position : -1;
|
||||
assertEq(text.indexOf(needle, start), expected);
|
||||
assertEq(text.includes(needle, start), expected !== -1);
|
||||
}
|
||||
assertEq(text.indexOf(needle + "b"), -1);
|
||||
assertEq(text.indexOf(needle + "a"), position < length ? position : -1);
|
||||
}
|
||||
}
|
||||
var latin1 = "\0".repeat(length) + "\xff";
|
||||
assertEq(latin1.indexOf("\u0100"), -1); // Must not narrow to NUL on x86.
|
||||
assertEq(latin1.indexOf("\uffff"), -1); // Must not narrow to 0xff on x86.
|
||||
var wide = "\u0100" + latin1;
|
||||
assertEq(wide.indexOf("\xff"), length + 1);
|
||||
assertEq(wide.indexOf(latin1), 1);
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ UNIFIED_SOURCES += [
|
|||
'testBug604087.cpp',
|
||||
'testCallArgs.cpp',
|
||||
'testCallNonGenericMethodOnProxy.cpp',
|
||||
'testCharacterOperations.cpp',
|
||||
'testChromeBuffer.cpp',
|
||||
'testClassGetter.cpp',
|
||||
'testCloneScript.cpp',
|
||||
|
|
|
|||
145
js/src/jsapi-tests/testCharacterOperations.cpp
Normal file
145
js/src/jsapi-tests/testCharacterOperations.cpp
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "vm/CharacterOperations.h"
|
||||
|
||||
#include <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,6 +38,7 @@
|
|||
#include "js/UniquePtr.h"
|
||||
#include "unicode/uchar.h"
|
||||
#include "unicode/unorm2.h"
|
||||
#include "vm/CharacterOperations.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/Opcodes.h"
|
||||
|
|
@ -1640,7 +1641,11 @@ FirstCharMatcherUnrolled(const TextChar* text, uint32_t n, const PatChar pat)
|
|||
static const char*
|
||||
FirstCharMatcher8bit(const char* text, uint32_t n, const char pat)
|
||||
{
|
||||
#if defined(__clang__)
|
||||
#ifdef JS_HAS_SSE2_CHARACTER_OPERATIONS
|
||||
if (n >= 16)
|
||||
return FindCharacter(text, n, pat);
|
||||
#endif
|
||||
#if defined(__clang__)
|
||||
return FirstCharMatcherUnrolled<char, char>(text, n, pat);
|
||||
#else
|
||||
return reinterpret_cast<const char*>(memchr(text, pat, n));
|
||||
|
|
@ -1650,6 +1655,10 @@ FirstCharMatcher8bit(const char* text, uint32_t n, const char pat)
|
|||
static const char16_t*
|
||||
FirstCharMatcher16bit(const char16_t* text, uint32_t n, const char16_t pat)
|
||||
{
|
||||
#ifdef JS_HAS_SSE2_CHARACTER_OPERATIONS
|
||||
if (n >= 8)
|
||||
return FindCharacter(text, n, pat);
|
||||
#endif
|
||||
#if defined(XP_DARWIN) || defined(XP_WIN)
|
||||
/*
|
||||
* Performance of memchr is horrible in OSX. Windows is better,
|
||||
|
|
@ -1734,18 +1743,14 @@ StringMatch(const TextChar* text, uint32_t textLen, const PatChar* pat, uint32_t
|
|||
return -1;
|
||||
|
||||
#if defined(__i386__) || defined(_M_IX86) || defined(__i386)
|
||||
/*
|
||||
* Given enough registers, the unrolled loop below is faster than the
|
||||
* following loop. 32-bit x86 does not have enough registers.
|
||||
*/
|
||||
// Avoid the generic substring matcher for a single character on x86.
|
||||
// FindCharacter uses SSE2 where available, including mixed encodings.
|
||||
if (patLen == 1) {
|
||||
const PatChar p0 = *pat;
|
||||
const TextChar* end = text + textLen;
|
||||
for (const TextChar* c = text; c != end; ++c) {
|
||||
if (*c == p0)
|
||||
return c - text;
|
||||
}
|
||||
return -1;
|
||||
// A two-byte needle cannot match Latin1 text if it exceeds 0xff.
|
||||
if (sizeof(TextChar) == 1 && uint32_t(*pat) > 0xff)
|
||||
return -1;
|
||||
const TextChar* match = FindCharacter(text, textLen, TextChar(*pat));
|
||||
return match ? int(match - text) : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -711,7 +711,17 @@ case "$target" in
|
|||
if test "$AS_BIN"; then
|
||||
AS="$(basename "$AS_BIN")"
|
||||
fi
|
||||
AR='lib'
|
||||
if test -n "$CLANG_CL"; then
|
||||
dnl Microsoft lib.exe cannot archive LLVM bitcode from ThinLTO.
|
||||
AC_PATH_PROG(LLVM_LIB, llvm-lib.exe)
|
||||
if test -z "$LLVM_LIB"; then
|
||||
AC_MSG_ERROR([clang-cl requires llvm-lib.exe on PATH])
|
||||
fi
|
||||
dnl expandlibs invokes AR from native Windows Python.
|
||||
AR="$(cd "$(dirname "$LLVM_LIB")" && pwd -W)/$(basename "$LLVM_LIB")"
|
||||
else
|
||||
AR='lib'
|
||||
fi
|
||||
AR_FLAGS='-NOLOGO -OUT:$@'
|
||||
AR_EXTRACT=
|
||||
RANLIB='echo not_ranlib'
|
||||
|
|
@ -2005,6 +2015,9 @@ if test "$ac_cv_struct_tm_zone_tm_gmtoff" = "yes" ; then
|
|||
fi
|
||||
fi # ! SKIP_COMPILER_CHECKS
|
||||
|
||||
dnl Windows skips the GCC probes above, but still needs LLVM PGO setup.
|
||||
MOZ_CLANG_CL_PGO
|
||||
|
||||
AC_DEFINE(CPP_THROW_NEW, [throw()])
|
||||
AC_LANG_C
|
||||
|
||||
|
|
|
|||
81
js/src/vm/CharacterOperations.h
Normal file
81
js/src/vm/CharacterOperations.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef vm_CharacterOperations_h
|
||||
#define vm_CharacterOperations_h
|
||||
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include <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,6 +15,7 @@
|
|||
|
||||
#include "gc/Marking.h"
|
||||
#include "js/UbiNode.h"
|
||||
#include "vm/CharacterOperations.h"
|
||||
#include "vm/SPSProfiler.h"
|
||||
|
||||
#include "jscntxtinlines.h"
|
||||
|
|
@ -1147,12 +1148,8 @@ js::NewDependentString(JSContext* cx, JSString* baseArg, size_t start, size_t le
|
|||
static bool
|
||||
CanStoreCharsAsLatin1(const char16_t* s, size_t length)
|
||||
{
|
||||
for (const char16_t* end = s + length; s < end; ++s) {
|
||||
if (*s > JSString::MAX_LATIN1_CHAR)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
static_assert(JSString::MAX_LATIN1_CHAR == 0xff, "Latin1 character range");
|
||||
return CharactersFitInLatin1(s, length);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
|
|||
|
|
@ -33,6 +33,12 @@
|
|||
|
||||
#define MOZALLOC_HAVE_XMALLOC
|
||||
|
||||
/* Workaround build problems with v142+ MSVC*/
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 5043)
|
||||
#endif
|
||||
|
||||
/* Workaround build problem with Sun Studio 12 */
|
||||
#if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
|
||||
# undef MOZ_MUST_USE
|
||||
|
|
|
|||
|
|
@ -392,11 +392,6 @@ pref("media.wave.play-stand-alone", false);
|
|||
pref("media.hardware-video-decoding.enabled", true);
|
||||
pref("media.hardware-video-decoding.force-enabled", false);
|
||||
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
pref("media.directshow.enabled", true);
|
||||
pref("media.directshow.h264.active", false);
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_FMP4
|
||||
pref("media.mp4.enabled", true);
|
||||
// Specifies whether the PDMFactory can create a test decoder that
|
||||
|
|
@ -4570,6 +4565,8 @@ pref("gfx.direct2d.force-enabled", false);
|
|||
|
||||
pref("layers.prefer-opengl", false);
|
||||
pref("layers.prefer-d3d9", false);
|
||||
// Experimental C GPU rectangle path. Requires D3D11.1 ClearView support and a restart.
|
||||
pref("layers.d3d11.gpu-rectangle-fills.enabled", false);
|
||||
// Enable fallback if d3d11 can't be used. See bug #1262187
|
||||
pref("layers.allow-d3d9-fallback", true);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,16 +5,6 @@
|
|||
|
||||
include('build/moz.configure/init.configure')
|
||||
|
||||
# DirectShow is the legacy Windows media backend. Keep this independent of
|
||||
# the old configure scripts so the backend is actually compiled on Windows.
|
||||
@depends(target)
|
||||
def directshow_enabled(target):
|
||||
if target.kernel == 'WINNT':
|
||||
return True
|
||||
|
||||
set_config('MOZ_DIRECTSHOW', directshow_enabled)
|
||||
set_define('MOZ_DIRECTSHOW', directshow_enabled)
|
||||
|
||||
# Note:
|
||||
# - Gecko-specific options and rules should go in toolkit/moz.configure.
|
||||
# - Firefox-specific options and rules should go in browser/moz.configure.
|
||||
|
|
|
|||
|
|
@ -876,7 +876,17 @@ case "$target" in
|
|||
if test "$AS_BIN"; then
|
||||
AS="$(basename "$AS_BIN")"
|
||||
fi
|
||||
AR='lib'
|
||||
if test -n "$CLANG_CL"; then
|
||||
dnl Microsoft lib.exe cannot archive LLVM bitcode from ThinLTO.
|
||||
AC_PATH_PROG(LLVM_LIB, llvm-lib.exe)
|
||||
if test -z "$LLVM_LIB"; then
|
||||
AC_MSG_ERROR([clang-cl requires llvm-lib.exe on PATH])
|
||||
fi
|
||||
dnl expandlibs invokes AR from native Windows Python.
|
||||
AR="$(cd "$(dirname "$LLVM_LIB")" && pwd -W)/$(basename "$LLVM_LIB")"
|
||||
else
|
||||
AR='lib'
|
||||
fi
|
||||
AR_FLAGS='-NOLOGO -OUT:$@'
|
||||
AR_EXTRACT=
|
||||
RANLIB='echo not_ranlib'
|
||||
|
|
@ -4547,6 +4557,9 @@ AC_SUBST(PROFILE_USE_LDFLAGS)
|
|||
|
||||
fi # ! SKIP_COMPILER_CHECKS
|
||||
|
||||
dnl Windows skips the GCC probes above, but still needs LLVM PGO setup.
|
||||
MOZ_CLANG_CL_PGO
|
||||
|
||||
AC_DEFINE(CPP_THROW_NEW, [throw()])
|
||||
AC_LANG_C
|
||||
|
||||
|
|
|
|||
|
|
@ -143,9 +143,6 @@ cppunittests-remote:
|
|||
jetpack-tests:
|
||||
cd $(topsrcdir)/addon-sdk/source && $(PYTHON) bin/cfx -b $(abspath $(browser_path)) --parseable testpkgs
|
||||
|
||||
pgo-profile-run:
|
||||
$(PYTHON) $(topsrcdir)/build/pgo/profileserver.py $(EXTRA_TEST_ARGS)
|
||||
|
||||
# Package up the tests and test harnesses
|
||||
include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
|
||||
|
||||
|
|
|
|||
|
|
@ -337,16 +337,6 @@ var snapshotFormatters = {
|
|||
addRowFromKey("features", "webgl2Extensions");
|
||||
addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
|
||||
addRowFromKey("features", "currentAudioBackend", "audioBackend");
|
||||
if ("directShowEnabled" in data) {
|
||||
addRow("features", "directShowEnabled",
|
||||
strings.GetStringFromName(data.directShowEnabled ? "yes" : "no"));
|
||||
delete data.directShowEnabled;
|
||||
}
|
||||
if ("directShowH264Active" in data) {
|
||||
addRow("features", "directShowH264Active",
|
||||
strings.GetStringFromName(data.directShowH264Active ? "yes" : "no"));
|
||||
delete data.directShowH264Active;
|
||||
}
|
||||
addRowFromKey("features", "direct2DEnabled", "#Direct2D");
|
||||
|
||||
if ("directWriteEnabled" in data) {
|
||||
|
|
|
|||
|
|
@ -173,9 +173,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
|||
'crypt32',
|
||||
'shell32',
|
||||
'ole32',
|
||||
'strmiids',
|
||||
'dmoguids',
|
||||
'msdmo',
|
||||
'version',
|
||||
'winspool',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ clearTypeParameters = ClearType Parameters
|
|||
compositing = Compositing
|
||||
hardwareH264 = Hardware H264 Decoding
|
||||
audioBackend = Audio Backend
|
||||
directShowEnabled = DirectShow H264 Backend Enabled
|
||||
directShowH264Active = DirectShow H264 Decoding Active
|
||||
mainThreadNoOMTC = main thread, no OMTC
|
||||
yes = Yes
|
||||
no = No
|
||||
|
|
|
|||
|
|
@ -358,8 +358,6 @@ var dataProviders = {
|
|||
} catch (e) {}
|
||||
|
||||
data.currentAudioBackend = winUtils.currentAudioBackend;
|
||||
data.directShowEnabled = Services.prefs.getBoolPref("media.directshow.enabled", false);
|
||||
data.directShowH264Active = Services.prefs.getBoolPref("media.directshow.h264.active", false);
|
||||
|
||||
if (!data.numAcceleratedWindows && gfxInfo) {
|
||||
#ifdef XP_WIN
|
||||
|
|
@ -553,3 +551,4 @@ var dataProviders = {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue