author | Ryan VanderMeulen <ryanvm@gmail.com> |
Fri, 11 Jul 2014 16:34:56 -0400 | |
changeset 193627 | 84bd8d9f4256c3130c1a8ff4d26fc2215056fd7a |
parent 193501 | 1ddc3c59e82b925e1caf4c90a64e7e2b4069562b (current diff) |
parent 193626 | 74bb8877ec94779a526ae0a4885e9303088e92d0 (diff) |
child 193633 | b0701d069bf9f97eba257ab426fd62015f9d7094 |
child 193688 | e0a49f64ef4f5e6d148abbca54bcacdd67f6b921 |
child 193773 | e2cda132334e4339405bf7307acce4ce0b5da412 |
push id | 27123 |
push user | ryanvm@gmail.com |
push date | Fri, 11 Jul 2014 20:35:05 +0000 |
treeherder | mozilla-central@84bd8d9f4256 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 33.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
content/base/public/nsContentUtils.h | file | annotate | diff | comparison | revisions | |
content/base/src/nsContentUtils.cpp | file | annotate | diff | comparison | revisions | |
content/media/gmp/gmp-api/gmp-video-errors.h | file | annotate | diff | comparison | revisions | |
security/manager/ssl/tests/gtest/Makefile.in | file | annotate | diff | comparison | revisions |
--- a/browser/components/loop/MozLoopService.jsm +++ b/browser/components/loop/MozLoopService.jsm @@ -4,23 +4,29 @@ "use strict"; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); -let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console; +Cu.import("resource://gre/modules/osfile.jsm", this); this.EXPORTED_SYMBOLS = ["MozLoopService"]; +XPCOMUtils.defineLazyModuleGetter(this, "console", + "resource://gre/modules/devtools/Console.jsm"); + XPCOMUtils.defineLazyModuleGetter(this, "injectLoopAPI", "resource:///modules/loop/MozLoopAPI.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "convertToRTCStatsReport", + "resource://gre/modules/media/RTCStatsReport.jsm"); + XPCOMUtils.defineLazyModuleGetter(this, "Chat", "resource:///modules/Chat.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils", "resource://services-common/utils.js"); XPCOMUtils.defineLazyModuleGetter(this, "CryptoUtils", "resource://services-crypto/utils.js"); @@ -28,16 +34,20 @@ XPCOMUtils.defineLazyModuleGetter(this, "resource://services-common/hawkclient.js"); XPCOMUtils.defineLazyModuleGetter(this, "deriveHawkCredentials", "resource://services-common/hawkrequest.js"); XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler", "resource:///modules/loop/MozLoopPushHandler.jsm"); +XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", + "@mozilla.org/uuid-generator;1", + "nsIUUIDGenerator"); + /** * Internal helper methods and state * * The registration is a two-part process. First we need to connect to * and register with the push server. Then we need to take the result of that * and register with the Loop server. */ let MozLoopServiceInternal = { @@ -299,25 +309,90 @@ let MozLoopServiceInternal = { map[key] = {}; map[key][property] = string.value; } return this._localizedStrings = map; }, /** + * Saves loop logs to the saved-telemetry-pings folder. + * + * @param {Object} pc The peerConnection in question. + */ + stageForTelemetryUpload: function(window, pc) { + window.WebrtcGlobalInformation.getAllStats(allStats => { + let internalFormat = allStats.reports[0]; // filtered on pc.id + window.WebrtcGlobalInformation.getLogging('', logs => { + let report = convertToRTCStatsReport(internalFormat); + let logStr = ""; + logs.forEach(s => { logStr += s + "\n"; }); + + // We have stats and logs. + + // Create worker job. ping = saved telemetry ping file header + payload + // + // Prepare payload according to https://wiki.mozilla.org/Loop/Telemetry + + let ai = Services.appinfo; + let uuid = uuidgen.generateUUID().toString(); + uuid = uuid.substr(1,uuid.length-2); // remove uuid curly braces + + let directory = OS.Path.join(OS.Constants.Path.profileDir, + "saved-telemetry-pings"); + let job = { + directory: directory, + filename: uuid + ".json", + ping: { + reason: "loop", + slug: uuid, + payload: { + ver: 1, + info: { + appUpdateChannel: ai.defaultUpdateChannel, + appBuildID: ai.appBuildID, + appName: ai.name, + appVersion: ai.version, + reason: "loop", + OS: ai.OS, + version: Services.sysinfo.getProperty("version") + }, + report: "ice failure", + connectionstate: pc.iceConnectionState, + stats: report, + localSdp: internalFormat.localSdp, + remoteSdp: internalFormat.remoteSdp, + log: logStr + } + } + }; + + // Send job to worker to do log sanitation, transcoding and saving to + // disk for pickup by telemetry on next startup, which then uploads it. + + let worker = new ChromeWorker("MozLoopWorker.js"); + worker.onmessage = function(e) { + console.log(e.data.ok ? + "Successfully staged loop report for telemetry upload." : + ("Failed to stage loop report. Error: " + e.data.fail)); + } + worker.postMessage(job); + }); + }, pc.id); + }, + + /** * Opens the chat window * * @param {Object} contentWindow The window to open the chat window in, may * be null. * @param {String} title The title of the chat window. * @param {String} url The page to load in the chat window. - * @param {String} mode May be "minimized" or undefined. */ - openChatWindow: function(contentWindow, title, url, mode) { + openChatWindow: function(contentWindow, title, url) { // So I guess the origin is the loop server!? let origin = this.loopServerUri; url = url.spec || url; let callback = chatbox => { // We need to use DOMContentLoaded as otherwise the injection will happen // in about:blank and then get lost. // Sadly we can't use chatbox.promiseChatLoaded() as promise chaining @@ -327,18 +402,43 @@ let MozLoopServiceInternal = { return; } chatbox.addEventListener("DOMContentLoaded", function loaded(event) { if (event.target != chatbox.contentDocument) { return; } chatbox.removeEventListener("DOMContentLoaded", loaded, true); - injectLoopAPI(chatbox.contentWindow); - }, true); + + let window = chatbox.contentWindow; + injectLoopAPI(window); + + let ourID = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; + + let onPCLifecycleChange = (pc, winID, type) => { + if (winID != ourID) { + return; + } + if (type == "iceconnectionstatechange") { + switch(pc.iceConnectionState) { + case "failed": + case "disconnected": + if (Services.telemetry.canSend || + Services.prefs.getBoolPref("toolkit.telemetry.test")) { + this.stageForTelemetryUpload(window, pc); + } + break; + } + } + }; + + let pc_static = new window.mozRTCPeerConnectionStatic(); + pc_static.registerPeerConnectionLifecycleCallback(onPCLifecycleChange); + }.bind(this), true); }; Chat.open(contentWindow, origin, title, url, undefined, undefined, callback); } }; /** * Public API
new file mode 100644 --- /dev/null +++ b/browser/components/loop/MozLoopWorker.js @@ -0,0 +1,161 @@ +/* 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/. */ + +/** + * A worker dedicated to loop-report sanitation and writing for MozLoopService. + */ + +"use strict"; + +importScripts("resource://gre/modules/osfile.jsm"); + +let File = OS.File; +let Encoder = new TextEncoder(); +let Counter = 0; + +const MAX_LOOP_LOGS = 5; +/** + * Communications with the controller. + * + * Accepts messages: + * { path: filepath, ping: data } + * + * Sends messages: + * { ok: true } + * { fail: serialized_form_of_OS.File.Error } + */ + +onmessage = function(e) { + if (++Counter > MAX_LOOP_LOGS) { + postMessage({ + fail: "Maximum " + MAX_LOOP_LOGS + "loop reports reached for this session" + }); + return; + } + + let directory = e.data.directory; + let filename = e.data.filename; + let ping = e.data.ping; + + // Anonymize data + resetIpMask(); + ping.payload.localSdp = redactSdp(ping.payload.localSdp); + ping.payload.remoteSdp = redactSdp(ping.payload.remoteSdp); + ping.payload.log = sanitizeLogs(ping.payload.log); + + let pingStr = anonymizeIPv4(sanitizeUrls(JSON.stringify(ping))); + + // Save to disk + let array = Encoder.encode(pingStr); + try { + File.makeDir(directory, + { unixMode: OS.Constants.S_IRWXU, ignoreExisting: true }); + File.writeAtomic(OS.Path.join(directory, filename), array); + postMessage({ ok: true }); + } catch (ex if ex instanceof File.Error) { + // Instances of OS.File.Error know how to serialize themselves + postMessage({fail: File.Error.toMsg(ex)}); + } +}; + +/** + * Mask upper 24-bits of ip address with fake numbers. Call resetIpMask() first. + */ + +let IpMap = {}; +let IpCount = 0; + +function resetIpMask() { + IpMap = {}; + IpCount = Math.floor(Math.random() * 16777215) + 1; +} + +/** + * Masks upper 24-bits of ip address with fake numbers. Grunt function. + * + * @param {DOMString} ip address + */ +function maskIp(ip) { + let isInvalidOrRfc1918or3927 = function(p1, p2, p3, p4) { + let invalid = octet => octet < 0 || octet > 255; + return invalid(p1) || invalid(p2) || invalid(p3) || invalid(p4) || + (p1 == 10) || + (p1 == 172 && p2 >= 16 && p2 <= 31) || + (p1 == 192 && p2 == 168) || + (p1 == 169 && p2 == 254); + }; + + let [p1, p2, p3, p4] = ip.split("."); + + if (isInvalidOrRfc1918or3927(p1, p2, p3, p4)) { + return ip; + } + let key = [p1, p2, p3].join(); + if (!IpMap[key]) { + do { + IpCount = (IpCount + 1049039) % 16777216; // + prime % 2^24 + p1 = (IpCount >> 16) % 256; + p2 = (IpCount >> 8) % 256; + p3 = IpCount % 256; + } while (isInvalidOrRfc1918or3927(p1, p2, p3, p4)); + IpMap[key] = p1 + "." + p2 + "." + p3; + } + return IpMap[key] + "." + p4; +} + +/** + * Partially masks ip numbers in input text. + * + * @param {DOMString} text Input text containing IP numbers as words. + */ +function anonymizeIPv4(text) { + return text.replace(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, + maskIp.bind(this)); +} + +/** + * Sanitizes any urls of session information, like + * + * - (id=31 url=https://call.services.mozilla.com/#call/ongoing/AQHYjqH_...) + * + (id=31 url=https://call.services.mozilla.com/#call/xxxx) + * + * - (id=35 url=about:loopconversation#incoming/1403134352854) + * + (id=35 url=about:loopconversation#incoming/xxxx) + * + * @param {DOMString} text The text. + */ +function sanitizeUrls(text) { + let trimUrl = url => url.replace(/(#call|#incoming).*/g, + (match, type) => type + "/xxxx"); + return text.replace(/\(id=(\d+) url=([^\)]+)\)/g, + (match, id, url) => + "(id=" + id + " url=" + trimUrl(url) + ")"); +} + +/** + * Removes privacy sensitive information from SDP input text outright, like + * + * a=fingerprint:sha-256 E9:DE:6A:FE:2A:2F:05: etc. + * a=identity ... + * + * Redacts lines from match to EOL. Assumes \r\n\ linebreaks. + * + * @param {DOMString} sdp The sdp text. + */ +let redactSdp = sdp => sdp.replace(/\r\na=(fingerprint|identity):.*?\r\n/g, + "\r\n"); + +/** + * Sanitizes log text of sensitive information, like + * + * - srflx(IP4:192.168.1.3:60348/UDP|turn402-oak.tokbox.com:3478) + * + srflx(IP4:192.168.1.3:60348/UDP|xxxx.xxx) + * + * @param {DOMString} log The log text. + */ +function sanitizeLogs(log) { + let rex = /(srflx|relay)\(IP4:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}\/(UDP|TCP)\|[^\)]+\)/g; + + return log.replace(rex, match => match.replace(/\|[^\)]+\)/, "|xxxx.xxx)")); +}
--- a/browser/components/loop/moz.build +++ b/browser/components/loop/moz.build @@ -9,9 +9,10 @@ JAR_MANIFESTS += ['jar.mn'] JS_MODULES_PATH = 'modules/loop' XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini'] EXTRA_JS_MODULES += [ 'MozLoopAPI.jsm', 'MozLoopPushHandler.jsm', 'MozLoopService.jsm', + 'MozLoopWorker.js', ]
--- a/browser/modules/WebappManager.jsm +++ b/browser/modules/WebappManager.jsm @@ -129,55 +129,49 @@ this.WebappManager = { "webapps-notification-icon"); let progressMeter = chromeDoc.createElement("progressmeter"); progressMeter.setAttribute("mode", "undetermined"); popupProgressContent.appendChild(progressMeter); let manifestURL = aData.app.manifestURL; - let cleanup = () => { + let nativeApp = new NativeApp(aData.app, jsonManifest, + aData.app.categories); + + this.installations[manifestURL] = Promise.defer(); + this.installations[manifestURL].promise.then(() => { + notifyInstallSuccess(aData.app, nativeApp, bundle); + }, (error) => { + Cu.reportError("Error installing webapp: " + error); + }).then(() => { popupProgressContent.removeChild(progressMeter); delete this.installations[manifestURL]; if (Object.getOwnPropertyNames(this.installations).length == 0) { notification.remove(); } - }; - - this.installations[manifestURL] = Promise.defer(); - this.installations[manifestURL].promise.then(null, (error) => { - Cu.reportError("Error installing webapp: " + error); - cleanup(); }); - let nativeApp = new NativeApp(aData.app, jsonManifest, - aData.app.categories); let localDir; try { localDir = nativeApp.createProfile(); } catch (ex) { - Cu.reportError("Error installing webapp: " + ex); DOMApplicationRegistry.denyInstall(aData); - cleanup(); return; } DOMApplicationRegistry.confirmInstall(aData, localDir, - (aApp, aManifest, aZipPath) => Task.spawn((function*() { + Task.async(function*(aApp, aManifest, aZipPath) { try { yield nativeApp.install(aApp, aManifest, aZipPath); - yield this.installations[manifestURL].promise; - notifyInstallSuccess(aApp, nativeApp, bundle); } catch (ex) { Cu.reportError("Error installing webapp: " + ex); - // TODO: Notify user that the installation has failed - } finally { - cleanup(); + throw ex; } - }).bind(this)) + }) ); } }; let requestingURI = chromeWin.makeURI(aData.from); let manifest = new ManifestHelper(jsonManifest, aData.app.origin); let host;
--- a/build/autoconf/ffi.m4 +++ b/build/autoconf/ffi.m4 @@ -83,22 +83,18 @@ if test -z "$BUILDING_JS" -o -n "$JS_STA ac_configure_args="$ac_configure_args CC=gcc CFLAGS=-maix32" else ac_configure_args="$ac_configure_args CC=gcc CFLAGS=-maix64" fi fi # Use a separate cache file for libffi, since it does things differently # from our configure. - mkdir -p $_objdir/js/src/ctypes/libffi - old_cache_file=$cache_file - cache_file=$_objdir/js/src/ctypes/libffi/config.cache old_config_files=$CONFIG_FILES unset CONFIG_FILES AC_OUTPUT_SUBDIRS(js/src/ctypes/libffi) - cache_file=$old_cache_file ac_configure_args="$_SUBDIR_CONFIG_ARGS" CONFIG_FILES=$old_config_files fi fi ])
--- a/build/autoconf/hooks.m4 +++ b/build/autoconf/hooks.m4 @@ -13,43 +13,55 @@ changequote([, ])dnl dnl Wrap AC_INIT_PREPARE to add the above trap. define([_MOZ_AC_INIT_PREPARE], defn([AC_INIT_PREPARE])) define([AC_INIT_PREPARE], [_MOZ_AC_INIT_PREPARE($1) MOZ_CONFIG_LOG_TRAP ]) dnl Disable the trap when running sub-configures. -define([_MOZ_AC_OUTPUT_SUBDIRS], defn([AC_OUTPUT_SUBDIRS])) -define([MOZ_SUBCONFIGURE_WRAP], -[ _CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} -case "$host" in -*-mingw*) +define(GEN_MOZ_AC_OUTPUT_SUBDIRS, [ +define([_MOZ_AC_OUTPUT_SUBDIRS], [ +patsubst($@, [$srcdir/$ac_config_dir], [$srcdir/$moz_config_srcdir]) +]) +]) +GEN_MOZ_AC_OUTPUT_SUBDIRS(defn([AC_OUTPUT_SUBDIRS])) + +define([AC_OUTPUT_SUBDIRS], +[trap '' EXIT +for moz_config_dir in $1; do + case "$moz_config_dir" in + *:*) + moz_config_srcdir=$(echo $moz_config_dir | awk -F: '{print [$]1}') + moz_config_dir=$(echo $moz_config_dir | awk -F: '{print [$]2}') + ;; + *) + moz_config_srcdir=$moz_config_dir + ;; + esac + _CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + case "$host" in + *-mingw*) _CONFIG_SHELL=$(cd $(dirname $_CONFIG_SHELL); pwd -W)/$(basename $_CONFIG_SHELL) if test ! -e "$_CONFIG_SHELL" -a -e "${_CONFIG_SHELL}.exe"; then _CONFIG_SHELL="${_CONFIG_SHELL}.exe" fi ;; -esac + esac -if test -d "$1"; then - (cd "$1"; $PYTHON $_topsrcdir/build/subconfigure.py dump "$_CONFIG_SHELL") -else - mkdir -p "$1" -fi -$2 -(cd "$1"; $PYTHON $_topsrcdir/build/subconfigure.py adjust $ac_sub_configure) -]) - -define([AC_OUTPUT_SUBDIRS], -[trap '' EXIT -for moz_config_dir in $1; do - MOZ_SUBCONFIGURE_WRAP([$moz_config_dir],[ - _MOZ_AC_OUTPUT_SUBDIRS($moz_config_dir) - ]) + if test -d "$moz_config_dir"; then + (cd "$moz_config_dir"; eval $PYTHON $_topsrcdir/build/subconfigure.py dump "$_CONFIG_SHELL" $ac_configure_args) + else + mkdir -p "$moz_config_dir" + fi + _save_cache_file="$cache_file" + ifelse($2,,cache_file="$moz_config_dir/config.cache",cache_file="$2") + _MOZ_AC_OUTPUT_SUBDIRS($moz_config_dir) + cache_file="$_save_cache_file" + (cd "$moz_config_dir"; $PYTHON $_topsrcdir/build/subconfigure.py adjust $ac_sub_configure) done MOZ_CONFIG_LOG_TRAP ]) dnl Print error messages in config.log as well as stderr define([AC_MSG_ERROR], [{ echo "configure: error: $1" 1>&2; echo "configure: error: $1" 1>&5; exit 1; }])
--- a/build/autoconf/icu.m4 +++ b/build/autoconf/icu.m4 @@ -149,37 +149,18 @@ if test -z "$BUILDING_JS" -o -n "$JS_STA ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_BREAK_ITERATION" ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_IDNA" # we don't need to pass data to and from legacy char* APIs ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_CHARSET_IS_UTF8" # make sure to not accidentally pick up system-icu headers ICU_CPPFLAGS="$ICU_CPPFLAGS -I$icudir/common -I$icudir/i18n" ICU_CROSS_BUILD_OPT="" - ICU_SRCDIR="" - if test "$HOST_OS_ARCH" = "WINNT"; then - ICU_SRCDIR="--srcdir=$(cd $srcdir/intl/icu/source; pwd -W)" - fi if test "$CROSS_COMPILE"; then - # Building host tools. It is necessary to build target binary. - case "$HOST_OS_ARCH" in - Darwin) - ICU_TARGET=MacOSX - ;; - Linux) - ICU_TARGET=Linux - ;; - WINNT) - ICU_TARGET=MSYS/MSVC - ;; - DragonFly|FreeBSD|NetBSD|OpenBSD|GNU_kFreeBSD) - ICU_TARGET=BSD - ;; - esac # Remove _DEPEND_CFLAGS from HOST_FLAGS to avoid configure error HOST_ICU_CFLAGS="$HOST_CFLAGS" HOST_ICU_CXXFLAGS="$HOST_CXXFLAGS" HOST_ICU_CFLAGS=`echo $HOST_ICU_CFLAGS | sed "s|$_DEPEND_CFLAGS||g"` HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CFXXLAGS | sed "s|$_DEPEND_CFLAGS||g"` # ICU requires RTTI @@ -191,34 +172,30 @@ if test -z "$BUILDING_JS" -o -n "$JS_STA HOST_ICU_BUILD_OPTS="" if test -n "$MOZ_DEBUG"; then HOST_ICU_BUILD_OPTS="$HOST_ICU_BUILD_OPTS --enable-debug" fi abs_srcdir=`(cd $srcdir; pwd)` mkdir -p $_objdir/intl/icu/host - (cd $_objdir/intl/icu/host - MOZ_SUBCONFIGURE_WRAP([.],[ - AR="$HOST_AR" RANLIB="$HOST_RANLIB" \ - CC="$HOST_CC" CXX="$HOST_CXX" LD="$HOST_LD" \ - CFLAGS="$HOST_ICU_CFLAGS $HOST_OPTIMIZE_FLAGS" \ - CPPFLAGS="$ICU_CPPFLAGS" \ - CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMIZE_FLAGS" \ - LDFLAGS="$HOST_LDFLAGS" \ - $SHELL $abs_srcdir/intl/icu/source/runConfigureICU \ - $HOST_ICU_BUILD_OPTS \ - $ICU_TARGET \ - dnl Shell quoting is fun. - ${ICU_SRCDIR+"$ICU_SRCDIR"} \ - --enable-static --disable-shared \ - --enable-extras=no --enable-icuio=no --enable-layout=no \ - --enable-tests=no --enable-samples=no || exit 1 - ]) - ) || exit 1 + (export AR="$HOST_AR" + export RANLIB="$HOST_RANLIB" + export CC="$HOST_CC" + export CXX="$HOST_CXX" + export CPP="$HOST_CPP" + export LD="$HOST_LD" + export CFLAGS="$HOST_ICU_CFLAGS $HOST_OPTIMIZE_FLAGS" + export CPPFLAGS="$ICU_CPPFLAGS" + export CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMIZE_FLAGS" + export LDFLAGS="$HOST_LDFLAGS" + ac_configure_args="$HOST_ICU_BUILD_OPTS" + ac_configure_args="$ac_configure_args --enable-static --disable-shared --enable-extras=no --enable-icuio=no --enable-layout=no --enable-tests=no --enable-samples=no" + AC_OUTPUT_SUBDIRS(intl/icu/source:intl/icu/host) + ) || exit 1 # generate config/icucross.mk $GMAKE -C $_objdir/intl/icu/host/ config/icucross.mk # --with-cross-build requires absolute path ICU_HOST_PATH=`cd $_objdir/intl/icu/host && pwd` ICU_CROSS_BUILD_OPT="--with-cross-build=$ICU_HOST_PATH" ICU_TARGET_OPT="--build=$build --host=$target" else @@ -303,37 +280,26 @@ if test -z "$BUILDING_JS" -o -n "$JS_STA ICU_CXXFLAGS="$ICU_CXXFLAGS -DU_STATIC_IMPLEMENTATION" ICU_CFLAGS="$ICU_CFLAGS -DU_STATIC_IMPLEMENTATION" if test "$GNU_CC"; then ICU_CFLAGS="$ICU_CFLAGS -fvisibility=hidden" ICU_CXXFLAGS="$ICU_CXXFLAGS -fvisibility=hidden" fi fi - # We cannot use AC_OUTPUT_SUBDIRS since ICU tree is out of spidermonkey. - # When using AC_OUTPUT_SUBDIRS, objdir of ICU is out of objdir - # due to relative path. - # If building ICU moves into root of mozilla tree, we can use - # AC_OUTPUT_SUBDIR instead. - mkdir -p $_objdir/intl/icu/target - (cd $_objdir/intl/icu/target - MOZ_SUBCONFIGURE_WRAP([.],[ - AR="$AR" CC="$CC" CXX="$CXX" LD="$LD" \ - ARFLAGS="$ARFLAGS" \ - CPPFLAGS="$ICU_CPPFLAGS $CPPFLAGS" \ - CFLAGS="$ICU_CFLAGS" \ - CXXFLAGS="$ICU_CXXFLAGS" \ - LDFLAGS="$ICU_LDFLAGS $LDFLAGS" \ - $SHELL $_topsrcdir/intl/icu/source/configure \ - $ICU_BUILD_OPTS \ - $ICU_CROSS_BUILD_OPT \ - $ICU_LINK_OPTS \ - ${ICU_SRCDIR+"$ICU_SRCDIR"} \ - $ICU_TARGET_OPT \ - --disable-extras --disable-icuio --disable-layout \ - --disable-tests --disable-samples || exit 1 - ]) + (export AR="$AR" + export CC="$CC" + export CXX="$CXX" + export LD="$LD" + export ARFLAGS="$ARFLAGS" + export CPPFLAGS="$ICU_CPPFLAGS $CPPFLAGS" + export CFLAGS="$ICU_CFLAGS" + export CXXFLAGS="$ICU_CXXFLAGS" + export LDFLAGS="$ICU_LDFLAGS $LDFLAGS" + ac_configure_args="$ICU_BUILD_OPTS $ICU_CROSS_BUILD_OPT $ICU_LINK_OPTS $ICU_TARGET_OPT" + ac_configure_args="$ac_configure_args --disable-extras --disable-icuio --disable-layout --disable-tests --disable-samples" + AC_OUTPUT_SUBDIRS(intl/icu/source:intl/icu/target) ) || exit 1 fi fi ])
--- a/build/sanitizers/lsan_suppressions.txt +++ b/build/sanitizers/lsan_suppressions.txt @@ -60,19 +60,16 @@ leak:event_base_once leak:nsLocalFile::OpenNSPRFileDesc # Bug 1023585 - Leak of array buffer in JSStructuredCloneWriter::transferOwnership(). m1 leak:AllocateArrayBufferContents # Bug 1022010 - Small leak under _render_glyph_outline. bc1 leak:_render_glyph_outline -# Bug 1022954 - ScriptSource leaks sourceMapURL_ sometimes. dt -leak:ScriptSource::setSourceMapURL - # Bug 1023548 - Small leak under SECITEM_AllocItem_Util. bc1, bc3 leak:SECITEM_AllocItem_Util # This is a one-time leak, so it is probably okay to ignore. bc1, oth leak:GlobalPrinters::InitializeGlobalPrinters leak:nsPSPrinterList::GetPrinterList # Bug 1028456 - More leaks with _PR_Getfd, in nsLocalFile::CopyToNative and do_create. bc1, bc3
--- a/build/subconfigure.py +++ b/build/subconfigure.py @@ -1,15 +1,16 @@ # 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/. # This script is used to capture the content of config.status-generated # files and subsequently restore their timestamp if they haven't changed. +import argparse import os import re import subprocess import sys import pickle class File(object): def __init__(self, path): @@ -55,34 +56,64 @@ PRECIOUS_VARS = set([ # Autoconf, in some of the sub-configures used in the tree, likes to error # out when "precious" variables change in value. The solution it gives to # straighten things is to either run make distclean or remove config.cache. # There's no reason not to do the latter automatically instead of failing, # doing the cleanup (which, on buildbots means a full clobber), and # restarting from scratch. -def maybe_clear_cache(): +def maybe_clear_cache(args): + parser = argparse.ArgumentParser() + parser.add_argument('--target', type=str) + parser.add_argument('--host', type=str) + parser.add_argument('--build', type=str) + args, others = parser.parse_known_args(args) + env = dict(os.environ) + for kind in ('target', 'host', 'build'): + arg = getattr(args, kind) + if arg is not None: + env['%s_alias' % kind] = arg + # configure can take variables assignments in its arguments, and that + # overrides whatever is in the environment. + for arg in others: + if arg[:1] != '-' and '=' in arg: + key, value = arg.split('=', 1) + env[key] = value + comment = re.compile(r'^\s+#') cache = {} with open('config.cache') as f: - for line in f.readlines(): + for line in f: if not comment.match(line) and '=' in line: - key, value = line.split('=', 1) + key, value = line.rstrip(os.linesep).split('=', 1) + # If the value is quoted, unquote it + if value[:1] == "'": + value = value[1:-1].replace("'\\''", "'") cache[key] = value for precious in PRECIOUS_VARS: - entry = 'ac_cv_env_%s_value' % precious - if entry in cache and (not precious in os.environ or os.environ[precious] != cache[entry]): + # If there is no entry at all for that precious variable, then + # its value is not precious for that particular configure. + if 'ac_cv_env_%s_set' % precious not in cache: + continue + is_set = cache.get('ac_cv_env_%s_set' % precious) == 'set' + value = cache.get('ac_cv_env_%s_value' % precious) if is_set else None + if value != env.get(precious): + print 'Removing config.cache because of %s value change from:' \ + % precious + print ' %s' % (value if value is not None else 'undefined') + print 'to:' + print ' %s' % env.get(precious, 'undefined') os.remove('config.cache') return -def dump(dump_file, shell): +def dump(dump_file, shell, args): if os.path.exists('config.cache'): - maybe_clear_cache() + maybe_clear_cache(args) if not os.path.exists('config.status'): if os.path.exists(dump_file): os.remove(dump_file) return config_files = [File('config.status')] # Scan the config.status output for information about configuration files @@ -127,11 +158,11 @@ def adjust(dump_file, configure): os.remove(dump_file) CONFIG_DUMP = 'config_files.pkl' if __name__ == '__main__': if sys.argv[1] == 'dump': - dump(CONFIG_DUMP, sys.argv[2]) + dump(CONFIG_DUMP, sys.argv[2], sys.argv[3:]) elif sys.argv[1] == 'adjust': adjust(CONFIG_DUMP, sys.argv[2] if len(sys.argv) > 2 else None)
--- a/build/valgrind/mach_commands.py +++ b/build/valgrind/mach_commands.py @@ -121,38 +121,45 @@ class MachCommands(MachCommandBase): # MACHTYPE is an odd bash-only environment variable that doesn't # show up in os.environ, so we have to get it another way. machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip() supps_file2 = os.path.join(supps_dir, machtype + '.sup') if os.path.isfile(supps_file2): valgrind_args.append('--suppressions=' + supps_file2) exitcode = None + timeout = 1100 try: runner = FirefoxRunner(profile=profile, binary=self.get_binary_path(), cmdargs=firefox_args, env=env, process_args=kp_kwargs) runner.start(debug_args=valgrind_args) - exitcode = runner.wait() + # This timeout is slightly less than the no-output timeout on + # TBPL, so we'll timeout here first and give an informative + # message. + exitcode = runner.wait(timeout=timeout) finally: errs = outputHandler.error_count supps = outputHandler.suppression_count if errs != supps: status = 1 # turns the TBPL job orange - print('TEST-UNEXPECTED-FAILURE | valgrind-test | error parsing:', errs, "errors seen, but", supps, "generated suppressions seen") + print('TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {} errors seen, but {} generated suppressions seen'.format(errs, supps)) elif errs == 0: status = 0 print('TEST-PASS | valgrind-test | valgrind found no errors') else: status = 1 # turns the TBPL job orange # We've already printed details of the errors. - if exitcode != 0: + if exitcode == None: + status = 2 # turns the TBPL job red + print('TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out (reached {} second limit)'.format(timeout)) + elif exitcode != 0: status = 2 # turns the TBPL job red print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind') httpd.stop() return status
--- a/config/config.mk +++ b/config/config.mk @@ -69,16 +69,17 @@ endif TIERS \ TOOL_DIRS \ XPCSHELL_TESTS \ XPIDL_MODULE \ $(NULL) _DEPRECATED_VARIABLES := \ ANDROID_RESFILES \ + EXPORT_LIBRARY \ LIBXUL_LIBRARY \ MOCHITEST_A11Y_FILES \ MOCHITEST_BROWSER_FILES \ MOCHITEST_BROWSER_FILES_PARTS \ MOCHITEST_CHROME_FILES \ MOCHITEST_FILES \ MOCHITEST_FILES_PARTS \ MOCHITEST_METRO_FILES \ @@ -340,17 +341,16 @@ endif endif # If we are building this component into an extension/xulapp, it cannot be # statically linked. In the future we may want to add a xulapp meta-component # build option. ifdef XPI_NAME ifdef IS_COMPONENT -EXPORT_LIBRARY= FORCE_STATIC_LIB= FORCE_SHARED_LIB=1 endif endif ifndef SHARED_LIBRARY_NAME ifdef LIBRARY_NAME SHARED_LIBRARY_NAME=$(LIBRARY_NAME)
--- a/config/makefiles/target_binaries.mk +++ b/config/makefiles/target_binaries.mk @@ -1,28 +1,15 @@ # -*- makefile -*- # vim:set ts=8 sw=8 sts=8 noet: # # 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/. -ifdef EXPORT_LIBRARY -ifeq ($(EXPORT_LIBRARY),1) -ifdef IS_COMPONENT -EXPORT_LIBRARY = $(DEPTH)/staticlib/components -else -EXPORT_LIBRARY = $(DEPTH)/staticlib -endif -else -# If EXPORT_LIBRARY has a value, we'll be installing there. We also need to cleanup there -GARBAGE += $(foreach lib,$(LIBRARY),$(EXPORT_LIBRARY)/$(lib)) -endif -endif # EXPORT_LIBRARY - binaries libs:: $(SUBMAKEFILES) $(TARGETS) ifndef NO_DIST_INSTALL ifdef SHARED_LIBRARY ifdef IS_COMPONENT $(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)/components ifndef NO_COMPONENTS_MANIFEST $(call py_action,buildlist,$(FINAL_TARGET)/chrome.manifest 'manifest components/components.manifest') $(call py_action,buildlist,$(FINAL_TARGET)/components/components.manifest 'binary-component $(SHARED_LIBRARY)') @@ -36,22 +23,16 @@ ifndef NO_DIST_INSTALL ifneq (,$(strip $(PROGRAM)$(SIMPLE_PROGRAMS))) PROGRAMS_EXECUTABLES = $(SIMPLE_PROGRAMS) $(PROGRAM) PROGRAMS_DEST ?= $(FINAL_TARGET) PROGRAMS_TARGET := binaries libs INSTALL_TARGETS += PROGRAMS endif ifdef LIBRARY -ifdef EXPORT_LIBRARY -LIBRARY_FILES = $(LIBRARY) -LIBRARY_DEST ?= $(EXPORT_LIBRARY) -LIBRARY_TARGET = binaries libs -INSTALL_TARGETS += LIBRARY -endif ifdef DIST_INSTALL ifdef IS_COMPONENT $(error Shipping static component libs makes no sense.) else DIST_LIBRARY_FILES = $(LIBRARY) DIST_LIBRARY_DEST ?= $(DIST)/lib DIST_LIBRARY_TARGET = binaries libs INSTALL_TARGETS += DIST_LIBRARY
--- a/config/rules.mk +++ b/config/rules.mk @@ -828,17 +828,17 @@ endif $(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(EXTRA_DEPS) $(GLOBAL_DEPS) $(REPORT_BUILD) $(RM) $(LIBRARY) $(EXPAND_AR) $(AR_FLAGS) $(OBJS) $(SHARED_LIBRARY_LIBS) $(filter-out %.$(LIB_SUFFIX),$(LIBRARY)): $(filter %.$(LIB_SUFFIX),$(LIBRARY)) $(OBJS) $(EXTRA_DEPS) $(GLOBAL_DEPS) # When we only build a library descriptor, blow out any existing library $(REPORT_BUILD) - $(if $(filter %.$(LIB_SUFFIX),$(LIBRARY)),,$(RM) $(REAL_LIBRARY) $(EXPORT_LIBRARY:%=%/$(REAL_LIBRARY))) + $(if $(filter %.$(LIB_SUFFIX),$(LIBRARY)),,$(RM) $(REAL_LIBRARY)) $(EXPAND_LIBS_GEN) -o $@ $(OBJS) $(SHARED_LIBRARY_LIBS) ifeq ($(OS_ARCH),WINNT) # Import libraries are created by the rules creating shared libraries. # The rules to copy them to $(DIST)/lib depend on $(IMPORT_LIBRARY), # but make will happily consider the import library before it is refreshed # when rebuilding the corresponding shared library. Defining an empty recipe # for import libraries forces make to wait for the shared library recipe to
--- a/configure.in +++ b/configure.in @@ -8778,17 +8778,16 @@ AC_SUBST(OBJ_SUFFIX) AC_SUBST(BIN_SUFFIX) AC_SUBST(ASM_SUFFIX) AC_SUBST(IMPORT_LIB_SUFFIX) AC_SUBST(USE_N32) AC_SUBST(CC_VERSION) AC_SUBST(CXX_VERSION) AC_SUBST(MSMANIFEST_TOOL) AC_SUBST(NS_ENABLE_TSF) -AC_SUBST(MOZ_APP_COMPONENT_LIBS) AC_SUBST(MOZ_APP_EXTRA_LIBS) AC_SUBST(MOZ_WAVE) AC_SUBST(MOZ_VORBIS) AC_SUBST(MOZ_TREMOR) AC_SUBST(MOZ_OPUS) AC_SUBST(MOZ_WEBM) AC_SUBST(MOZ_WMF) @@ -9045,31 +9044,28 @@ if test -z "$MOZ_NATIVE_JEMALLOC" -a "$M fi EXTRA_CFLAGS="$CFLAGS" for var in AS CC CXX CPP LD AR RANLIB STRIP CPPFLAGS EXTRA_CFLAGS LDFLAGS; do ac_configure_args="$ac_configure_args $var='`eval echo \\${${var}}`'" done if test "$CROSS_COMPILE"; then ac_configure_args="$ac_configure_args je_cv_static_page_shift=12" fi - _save_cache_file="$cache_file" - cache_file=$_objdir/memory/jemalloc/src/config.cache if ! test -e memory/jemalloc; then mkdir -p memory/jemalloc fi dnl jemalloc's configure uses $srcdir in AC_CONFIG_HEADERS, which syntax uses dnl colons, conflicting with the windows-style path in $srcdir. dnl Turn it into a msys path just for jemalloc's configure _save_srcdir="$srcdir" srcdir=`cd $srcdir; pwd` AC_OUTPUT_SUBDIRS(memory/jemalloc/src) srcdir="$_save_srcdir" - cache_file="$_save_cache_file" ac_configure_args="$_SUBDIR_CONFIG_ARGS" fi # Run freetype configure script if test "$MOZ_TREE_FREETYPE"; then export CFLAGS="$CFLAGS $MOZ_DEBUG_FLAGS -std=c99" export CPPFLAGS="$CPPFLAGS $MOZ_DEBUG_FLAGS" @@ -9083,17 +9079,17 @@ if test "$MOZ_TREE_FREETYPE"; then export ZLIB_LIBS="$MOZ_ZLIB_LIBS " export CONFIG_FILES="unix-cc.mk:unix-cc.in unix-def.mk:unix-def.in freetype-config freetype2.pc:freetype2.in" ac_configure_args="$ac_configure_args --host=$target --disable-shared --with-pic=yes --with-zlib=yes --without-bzip2 --with-png=yes --without-harfbuzz" if ! test -e modules; then mkdir modules fi - AC_OUTPUT_SUBDIRS(modules/freetype2) + AC_OUTPUT_SUBDIRS(modules/freetype2,$cache_file) fi if test -z "$direct_nspr_config"; then dnl ======================================================== dnl = Setup a nice relatively clean build environment for dnl = sub-configures. dnl ======================================================== CC="$_SUBDIR_CC" @@ -9175,24 +9171,19 @@ if test -z "$MOZ_NATIVE_NSPR"; then if test -n "$MOZ_LINKER" -a "$ac_cv_func_dladdr" = no ; then # dladdr is supported by the new linker, even when the system linker doesn't # support it. Trick nspr into using dladdr when it's not supported. export CPPFLAGS="-include $_topsrcdir/mozglue/linker/dladdr.h $CPPFLAGS" fi export LDFLAGS="$LDFLAGS $NSPR_LDFLAGS" export CFLAGS="$CFLAGS $MOZ_FRAMEPTR_FLAGS" - # Use a separate cache file for NSPR since it uses autoconf 2.68. - _save_cache_file="$cache_file" - cache_file=$_objdir/nsprpub/config.cache - AC_OUTPUT_SUBDIRS(nsprpub) # .. and restore them - cache_file="$_save_cache_file" CFLAGS="$_SAVE_CFLAGS" CPPFLAGS="$_SAVE_CPPFLAGS" LDFLAGS="$_SAVE_LDFLAGS" ac_configure_args="$_SUBDIR_CONFIG_ARGS" fi dnl ======================================================== @@ -9272,17 +9263,17 @@ export STLPORT_LIBS export JS_STANDALONE=no export MOZ_LINKER export ZLIB_IN_MOZGLUE if ! test -e js; then mkdir js fi -AC_OUTPUT_SUBDIRS(js/src) +AC_OUTPUT_SUBDIRS(js/src,$cache_file) ac_configure_args="$_SUBDIR_CONFIG_ARGS" fi # COMPILE_ENVIRONMENT && !LIBXUL_SDK_DIR export WRITE_MOZINFO=1 dnl we need to run config.status after js/src subconfigure because we're dnl traversing its moz.build and we need its config.status for that. dnl However, writing our own config.status needs to happen before
--- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -472,18 +472,17 @@ public: static nsIScriptSecurityManager* GetSecurityManager() { return sSecurityManager; } /** * Get the ContentSecurityPolicy for a JS context. **/ - static bool GetContentSecurityPolicy(JSContext* aCx, - nsIContentSecurityPolicy** aCSP); + static bool GetContentSecurityPolicy(nsIContentSecurityPolicy** aCSP); // Returns the subject principal. Guaranteed to return non-null. May only // be called when nsContentUtils is initialized. static nsIPrincipal* SubjectPrincipal(); // Returns the prinipal of the given JS object. This may only be called on // the main thread for objects from the main thread's JSRuntime. static nsIPrincipal* ObjectPrincipal(JSObject* aObj);
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -6420,21 +6420,19 @@ nsContentUtils::FindInternalContentViewe } return docFactory.forget(); } return nullptr; } bool -nsContentUtils::GetContentSecurityPolicy(JSContext* aCx, - nsIContentSecurityPolicy** aCSP) +nsContentUtils::GetContentSecurityPolicy(nsIContentSecurityPolicy** aCSP) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - MOZ_ASSERT(aCx == GetCurrentJSContext()); nsCOMPtr<nsIContentSecurityPolicy> csp; nsresult rv = SubjectPrincipal()->GetCsp(getter_AddRefs(csp)); if (NS_FAILED(rv)) { NS_ERROR("CSP: Failed to get CSP from principal."); return false; }
--- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -935,17 +935,17 @@ public: size_t size = DOMMemoryFileDataOwnerMallocSizeOf(owner->mData); if (size < LARGE_OBJECT_MIN_SIZE) { smallObjectsTotal += size; } else { SHA1Sum sha1; sha1.update(owner->mData, owner->mLength); - uint8_t digest[SHA1Sum::HashSize]; // SHA1 digests are 20 bytes long. + uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long. sha1.finish(digest); nsAutoCString digestString; for (size_t i = 0; i < sizeof(digest); i++) { digestString.AppendPrintf("%02x", digest[i]); } nsresult rv = aCallback->Callback(
--- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -1466,19 +1466,21 @@ nsScriptLoader::PrepareLoadedRequest(nsS if (httpChannel) { bool requestSucceeded; rv = httpChannel->GetRequestSucceeded(&requestSucceeded); if (NS_SUCCEEDED(rv) && !requestSucceeded) { return NS_ERROR_NOT_AVAILABLE; } nsAutoCString sourceMapURL; - httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL); - aRequest->mHasSourceMapURL = true; - aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL); + rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL); + if (NS_SUCCEEDED(rv)) { + aRequest->mHasSourceMapURL = true; + aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL); + } } nsCOMPtr<nsIChannel> channel = do_QueryInterface(req); // If this load was subject to a CORS check; don't flag it with a // separate origin principal, so that it will treat our document's // principal as the origin principal if (aRequest->mCORSMode == CORS_NONE) { rv = nsContentUtils::GetSecurityManager()->
--- a/content/base/test/chrome/nochrome_bug765993.js +++ b/content/base/test/chrome/nochrome_bug765993.js @@ -1,4 +1,4 @@ -//@ sourceMappingURL=bar.js.map +//# sourceMappingURL=bar.js.map // Define a single function to prevent script source from being gc'd function foo() {}
--- a/content/media/MediaDecoderStateMachine.cpp +++ b/content/media/MediaDecoderStateMachine.cpp @@ -8,16 +8,17 @@ #include "windows.h" #include "mmsystem.h" #endif #include "mozilla/DebugOnly.h" #include <stdint.h> #include "MediaDecoderStateMachine.h" +#include "MediaDecoderStateMachineScheduler.h" #include "AudioSink.h" #include "nsTArray.h" #include "MediaDecoder.h" #include "MediaDecoderReader.h" #include "mozilla/mozalloc.h" #include "VideoUtils.h" #include "mozilla/dom/TimeRanges.h" #include "nsDeque.h" @@ -163,18 +164,21 @@ static TimeDuration UsecsToDuration(int6 static int64_t DurationToUsecs(TimeDuration aDuration) { return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S); } MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, MediaDecoderReader* aReader, bool aRealTime) : mDecoder(aDecoder), + mScheduler(new MediaDecoderStateMachineScheduler( + aDecoder->GetReentrantMonitor(), + &MediaDecoderStateMachine::TimeoutExpired, + MOZ_THIS_IN_INITIALIZER_LIST(), aRealTime)), mState(DECODER_STATE_DECODING_METADATA), - mInRunningStateMachine(false), mSyncPointInMediaStream(-1), mSyncPointInDecodedStream(-1), mPlayDuration(0), mStartTime(-1), mEndTime(-1), mFragmentEndTime(-1), mReader(aReader), mCurrentFrameTime(0), @@ -193,40 +197,34 @@ MediaDecoderStateMachine::MediaDecoderSt mPositionChangeQueued(false), mAudioCompleted(false), mGotDurationFromMetaData(false), mDispatchedEventToDecode(false), mStopAudioThread(true), mQuickBuffering(false), mMinimizePreroll(false), mDecodeThreadWaiting(false), - mRealTime(aRealTime), mDispatchedDecodeMetadataTask(false), mDropAudioUntilNextDiscontinuity(false), mDropVideoUntilNextDiscontinuity(false), mDecodeToSeekTarget(false), mCurrentTimeBeforeSeek(0), - mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED), - mTimerId(0) + mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED) { MOZ_COUNT_CTOR(MediaDecoderStateMachine); NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); - // Only enable realtime mode when "media.realtime_decoder.enabled" is true. - if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false) - mRealTime = false; - mAmpleVideoFrames = std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3); - mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT_S; - mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS; - - mVideoPrerollFrames = mRealTime ? 0 : mAmpleVideoFrames / 2; - mAudioPrerollUsecs = mRealTime ? 0 : LOW_AUDIO_USECS * 2; + mBufferingWait = mScheduler->IsRealTime() ? 0 : BUFFERING_WAIT_S; + mLowDataThresholdUsecs = mScheduler->IsRealTime() ? 0 : LOW_DATA_THRESHOLD_USECS; + + mVideoPrerollFrames = mScheduler->IsRealTime() ? 0 : mAmpleVideoFrames / 2; + mAudioPrerollUsecs = mScheduler->IsRealTime() ? 0 : LOW_AUDIO_USECS * 2; #ifdef XP_WIN // Ensure high precision timers are enabled on Windows, otherwise the state // machine thread isn't woken up at reliable intervals to set the next frame, // and we drop frames while painting. Note that multiple calls to this // function per-process is OK, provided each call is matched by a corresponding // timeEndPeriod() call. timeBeginPeriod(1); @@ -236,18 +234,16 @@ MediaDecoderStateMachine::MediaDecoderSt MediaDecoderStateMachine::~MediaDecoderStateMachine() { MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); MOZ_COUNT_DTOR(MediaDecoderStateMachine); NS_ASSERTION(!mPendingWakeDecoder.get(), "WakeDecoder should have been revoked already"); MOZ_ASSERT(!mDecodeTaskQueue, "Should be released in SHUTDOWN"); - // No need to cancel the timer here for we've done that in SHUTDOWN. - MOZ_ASSERT(!mTimer, "Should be released in SHUTDOWN"); mReader = nullptr; #ifdef XP_WIN timeEndPeriod(1); #endif } bool MediaDecoderStateMachine::HasFutureAudio() { @@ -1058,34 +1054,25 @@ bool MediaDecoderStateMachine::IsPlaying nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor) { MOZ_ASSERT(NS_IsMainThread()); RefPtr<SharedThreadPool> decodePool(GetMediaDecodeThreadPool()); NS_ENSURE_TRUE(decodePool, NS_ERROR_FAILURE); - RefPtr<SharedThreadPool> stateMachinePool( - SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1)); - NS_ENSURE_TRUE(stateMachinePool, NS_ERROR_FAILURE); - mDecodeTaskQueue = new MediaTaskQueue(decodePool.forget()); NS_ENSURE_TRUE(mDecodeTaskQueue, NS_ERROR_FAILURE); MediaDecoderReader* cloneReader = nullptr; if (aCloneDonor) { cloneReader = aCloneDonor->mReader; } - mStateMachineThreadPool = stateMachinePool; - - nsresult rv; - mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = mTimer->SetTarget(GetStateMachineThread()); + nsresult rv = mScheduler->Init(); NS_ENSURE_SUCCESS(rv, rv); // Note: This creates a cycle, broken in shutdown. mMediaDecodedListener = new MediaDataDecodedListener<MediaDecoderStateMachine>(this, mDecodeTaskQueue); mReader->SetCallback(mMediaDecodedListener); mReader->SetTaskQueue(mDecodeTaskQueue); @@ -1337,18 +1324,18 @@ void MediaDecoderStateMachine::Shutdown( NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); // Once we've entered the shutdown state here there's no going back. ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); // Change state before issuing shutdown request to threads so those // threads can start exiting cleanly during the Shutdown call. DECODER_LOG(PR_LOG_DEBUG, "Changed state to SHUTDOWN"); - ScheduleStateMachine(); mState = DECODER_STATE_SHUTDOWN; + mScheduler->ScheduleAndShutdown(); if (mAudioSink) { mAudioSink->PrepareToShutdown(); } mDecoder->GetReentrantMonitor().NotifyAll(); } void MediaDecoderStateMachine::StartDecoding() { @@ -1748,16 +1735,17 @@ MediaDecoderStateMachine::StartAudioThre if (HasAudio() && !mAudioSink) { mAudioCompleted = false; mAudioSink = new AudioSink(this, mAudioStartTime, mInfo.mAudio, mDecoder->GetAudioChannel()); nsresult rv = mAudioSink->Init(); if (NS_FAILED(rv)) { DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because audio sink initialization failed"); mState = DECODER_STATE_SHUTDOWN; + mScheduler->ScheduleAndShutdown(); return rv; } mAudioSink->SetVolume(mVolume); mAudioSink->SetPlaybackRate(mPlaybackRate); mAudioSink->SetPreservesPitch(mPreservesPitch); } return NS_OK; @@ -1825,18 +1813,18 @@ MediaDecoderStateMachine::DecodeError() // Already shutdown. return; } // Change state to shutdown before sending error report to MediaDecoder // and the HTMLMediaElement, so that our pipeline can start exiting // cleanly during the sync dispatch below. DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN due to error"); - ScheduleStateMachine(); mState = DECODER_STATE_SHUTDOWN; + mScheduler->ScheduleAndShutdown(); mDecoder->GetReentrantMonitor().NotifyAll(); // Dispatch the event to call DecodeError synchronously. This ensures // we're in shutdown state by the time we exit the decode thread. // If we just moved to shutdown state here on the decode thread, we may // cause the state machine to shutdown/free memory without closing its // media stream properly, and we'll get callbacks from the media stream // causing a crash. @@ -1906,17 +1894,17 @@ nsresult MediaDecoderStateMachine::Decod AudioQueue().AddPopListener(decodeTask, mDecodeTaskQueue); } if (HasVideo()) { RefPtr<nsIRunnable> decodeTask( NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded)); VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue); } - if (mRealTime) { + if (mScheduler->IsRealTime()) { SetStartTime(0); res = FinishDecodeMetadata(); NS_ENSURE_SUCCESS(res, res); } else { if (HasAudio()) { ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor()); mReader->RequestAudioData(); } @@ -1935,17 +1923,17 @@ MediaDecoderStateMachine::FinishDecodeMe AssertCurrentThreadInMonitor(); NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); DECODER_LOG(PR_LOG_DEBUG, "Decoding Media Headers"); if (mState == DECODER_STATE_SHUTDOWN) { return NS_ERROR_FAILURE; } - if (!mRealTime) { + if (!mScheduler->IsRealTime()) { const VideoData* v = VideoQueue().PeekFront(); const AudioData* a = AudioQueue().PeekFront(); int64_t startTime = std::min<int64_t>(a ? a->mTime : INT64_MAX, v ? v->mTime : INT64_MAX); if (startTime == INT64_MAX) { startTime = 0; @@ -2169,27 +2157,31 @@ MediaDecoderStateMachine::SeekCompleted( UpdatePlaybackPositionInternal(newCurrentTime); if (mDecoder->GetDecodedStream()) { SetSyncPointForMediaStream(); } // Try to decode another frame to detect if we're at the end... DECODER_LOG(PR_LOG_DEBUG, "Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime); + // Prevent changes in playback position before 'seeked' is fired for we + // expect currentTime equals seek target in 'seeked' callback. + mScheduler->FreezeScheduling(); { ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC); } // Reset quick buffering status. This ensures that if we began the // seek while quick-buffering, we won't bypass quick buffering mode // if we need to buffer after the seek. mQuickBuffering = false; ScheduleStateMachine(); + mScheduler->ThawScheduling(); } // Runnable to dispose of the decoder and state machine on the main thread. class nsDecoderDisposeEvent : public nsRunnable { public: nsDecoderDisposeEvent(already_AddRefed<MediaDecoder> aDecoder, already_AddRefed<MediaDecoderStateMachine> aStateMachine) : mDecoder(aDecoder), mStateMachine(aStateMachine) {} @@ -2287,18 +2279,16 @@ nsresult MediaDecoderStateMachine::RunSt // state machine) needs to finish and be released in order to allow // that. So we dispatch an event to run after this event runner has // finished and released its monitor/references. That event then will // dispatch an event to the main thread to release the decoder and // state machine. GetStateMachineThread()->Dispatch( new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL); - mTimer->Cancel(); - mTimer = nullptr; return NS_OK; } case DECODER_STATE_DORMANT: { if (IsPlaying()) { StopPlayback(); } FlushDecoding(); @@ -2584,17 +2574,17 @@ void MediaDecoderStateMachine::AdvanceFr int64_t remainingTime = AUDIO_DURATION_USECS; NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time."); nsAutoPtr<VideoData> currentFrame; #ifdef PR_LOGGING int32_t droppedFrames = 0; #endif if (VideoQueue().GetSize() > 0) { VideoData* frame = VideoQueue().PeekFront(); - while (mRealTime || clock_time >= frame->mTime) { + while (mScheduler->IsRealTime() || clock_time >= frame->mTime) { mVideoFrameEndTime = frame->GetEndTime(); currentFrame = frame; #ifdef PR_LOGGING VERBOSE_LOG("discarding video frame %lld", frame->mTime); if (droppedFrames++) { VERBOSE_LOG("discarding video frame %lld (%d so far)", frame->mTime, droppedFrames-1); } #endif @@ -2924,128 +2914,53 @@ nsresult MediaDecoderStateMachine::CallR AssertCurrentThreadInMonitor(); NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread."); // If audio is being captured, stop the audio sink if it's running if (mAudioCaptured) { StopAudioThread(); } - MOZ_ASSERT(!mInRunningStateMachine, "State machine cycles must run in sequence!"); - mTimeout = TimeStamp(); - mInRunningStateMachine = true; - nsresult res = RunStateMachine(); - mInRunningStateMachine = false; - return res; + return RunStateMachine(); } -nsresult MediaDecoderStateMachine::TimeoutExpired(int aTimerId) +nsresult MediaDecoderStateMachine::TimeoutExpired(void* aClosure) { - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - NS_ASSERTION(OnStateMachineThread(), "Must be on state machine thread"); - mTimer->Cancel(); - if (mTimerId == aTimerId) { - return CallRunStateMachine(); - } else { - return NS_OK; - } + MediaDecoderStateMachine* p = static_cast<MediaDecoderStateMachine*>(aClosure); + return p->CallRunStateMachine(); } void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); DispatchAudioDecodeTaskIfNeeded(); DispatchVideoDecodeTaskIfNeeded(); } -class TimerEvent : public nsITimerCallback, public nsRunnable { - NS_DECL_THREADSAFE_ISUPPORTS -public: - TimerEvent(MediaDecoderStateMachine* aStateMachine, int aTimerId) - : mStateMachine(aStateMachine), mTimerId(aTimerId) {} - - NS_IMETHOD Run() MOZ_OVERRIDE { - return mStateMachine->TimeoutExpired(mTimerId); - } - - NS_IMETHOD Notify(nsITimer* aTimer) { - return mStateMachine->TimeoutExpired(mTimerId); - } -private: - ~TimerEvent() {} - - const nsRefPtr<MediaDecoderStateMachine> mStateMachine; - int mTimerId; -}; - -NS_IMPL_ISUPPORTS(TimerEvent, nsITimerCallback, nsIRunnable); - nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) { - AssertCurrentThreadInMonitor(); - NS_ABORT_IF_FALSE(GetStateMachineThread(), - "Must have a state machine thread to schedule"); - - if (mState == DECODER_STATE_SHUTDOWN) { - return NS_ERROR_FAILURE; - } - aUsecs = std::max<int64_t>(aUsecs, 0); - - TimeStamp timeout = TimeStamp::Now() + UsecsToDuration(aUsecs); - if (!mTimeout.IsNull() && timeout >= mTimeout) { - // We've already scheduled a timer set to expire at or before this time, - // or have an event dispatched to run the state machine. - return NS_OK; - } - - uint32_t ms = static_cast<uint32_t>((aUsecs / USECS_PER_MS) & 0xFFFFFFFF); - if (mRealTime && ms > 40) { - ms = 40; - } - - // Don't cancel the timer here for this function will be called from - // different threads. - - nsresult rv = NS_ERROR_FAILURE; - nsRefPtr<TimerEvent> event = new TimerEvent(this, mTimerId+1); - - if (ms == 0) { - // Dispatch a runnable to the state machine thread when delay is 0. - // It will has less latency than dispatching a runnable to the state - // machine thread which will then schedule a zero-delay timer. - rv = GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL); - } else if (OnStateMachineThread()) { - rv = mTimer->InitWithCallback(event, ms, nsITimer::TYPE_ONE_SHOT); - } else { - MOZ_ASSERT(false, "non-zero delay timer should be only scheduled in state machine thread"); - } - - if (NS_SUCCEEDED(rv)) { - mTimeout = timeout; - ++mTimerId; - } else { - NS_WARNING("Failed to schedule state machine"); - } - - return rv; + return mScheduler->Schedule(aUsecs); } bool MediaDecoderStateMachine::OnDecodeThread() const { return mDecodeTaskQueue->IsCurrentThreadIn(); } bool MediaDecoderStateMachine::OnStateMachineThread() const { - bool rv = false; - mStateMachineThreadPool->IsOnCurrentThread(&rv); - return rv; + return mScheduler->OnStateMachineThread(); } -nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() +nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() const { - return mStateMachineThreadPool->GetEventTarget(); + return mScheduler->GetStateMachineThread(); +} + +bool MediaDecoderStateMachine::IsStateMachineScheduled() const +{ + return mScheduler->IsScheduled(); } void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate) { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); NS_ASSERTION(aPlaybackRate != 0, "PlaybackRate == 0 should be handled before this function."); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
--- a/content/media/MediaDecoderStateMachine.h +++ b/content/media/MediaDecoderStateMachine.h @@ -95,16 +95,17 @@ class nsITimer; namespace mozilla { class AudioSegment; class VideoSegment; class MediaTaskQueue; class SharedThreadPool; class AudioSink; +class MediaDecoderStateMachineScheduler; // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to // GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime // implementation. #ifdef GetCurrentTime #undef GetCurrentTime #endif @@ -274,29 +275,30 @@ public: void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset); int64_t GetEndMediaTime() const { AssertCurrentThreadInMonitor(); return mEndTime; } // Returns the shared state machine thread. - nsIEventTarget* GetStateMachineThread(); + nsIEventTarget* GetStateMachineThread() const; // Calls ScheduleStateMachine() after taking the decoder lock. Also // notifies the decoder thread in case it's waiting on the decoder lock. void ScheduleStateMachineWithLockAndWakeDecoder(); // Schedules the shared state machine thread to run the state machine // in aUsecs microseconds from now, if it's not already scheduled to run // earlier, in which case the request is discarded. nsresult ScheduleStateMachine(int64_t aUsecs = 0); - // Timer function to implement ScheduleStateMachine(aUsecs). - nsresult TimeoutExpired(int aGeneration); + // Callback function registered with MediaDecoderStateMachineScheduler + // to run state machine cycles. + static nsresult TimeoutExpired(void* aClosure); // Set the media fragment end time. aEndTime is in microseconds. void SetFragmentEndTime(int64_t aEndTime); // Drop reference to decoder. Only called during shutdown dance. void BreakCycles() { if (mReader) { mReader->BreakCycles(); @@ -442,17 +444,17 @@ protected: // Dispatches an asynchronous event to update the media element's ready state. void UpdateReadyState(); // Resets playback timing data. Called when we seek, on the decode thread. void ResetPlayback(); // Orders the Reader to stop decoding, and blocks until the Reader // has stopped decoding and finished delivering samples, then calls - // ResetPlayback() to discard all enqueued data. + // ResetPlayback() to discard all enqueued data. void FlushDecoding(); // Returns the audio clock, if we have audio, or -1 if we don't. // Called on the state machine thread. int64_t GetAudioClock(); // Get the video stream position, taking the |playbackRate| change into // account. This is a position in the media, not the duration of the playback @@ -605,20 +607,17 @@ protected: // State machine thread run function. Defers to RunStateMachine(). nsresult CallRunStateMachine(); // Performs one "cycle" of the state machine. Polls the state, and may send // a video frame to be displayed, and generally manages the decode. Called // periodically via timer to ensure the video stays in sync. nsresult RunStateMachine(); - bool IsStateMachineScheduled() const { - AssertCurrentThreadInMonitor(); - return !mTimeout.IsNull(); - } + bool IsStateMachineScheduled() const; // Returns true if we're not playing and the decode thread has filled its // decode buffers and is waiting. We can shut the decode thread down in this // case as it may not be needed again. bool IsPausedAndDecoderWaiting(); // These return true if the respective stream's decode has not yet reached // the end of stream. @@ -645,16 +644,20 @@ protected: // dropped its reference to the decoder. This enables the state machine to // keep using the decoder's monitor until the state machine has finished // shutting down, without fear of the monitor being destroyed. After // shutting down, the state machine will then release this reference, // causing the decoder to be destroyed. This is accessed on the decode, // state machine, audio and main threads. nsRefPtr<MediaDecoder> mDecoder; + // Used to schedule state machine cycles. This should never outlive + // the life cycle of the state machine. + const nsAutoPtr<MediaDecoderStateMachineScheduler> mScheduler; + // Time at which the last video sample was requested. If it takes too long // before the sample arrives, we will increase the amount of audio we buffer. // This is necessary for legacy synchronous decoders to prevent underruns. TimeStamp mVideoDecodeStartTime; // Queue of audio frames. This queue is threadsafe, and is accessed from // the audio, decoder, state machine, and main threads. MediaQueue<AudioData> mAudioQueue; @@ -669,29 +672,16 @@ protected: // Accessed on state machine, audio, main, and AV thread. State mState; // The task queue in which we run decode tasks. This is referred to as // the "decode thread", though in practise tasks can run on a different // thread every time they're called. RefPtr<MediaTaskQueue> mDecodeTaskQueue; - RefPtr<SharedThreadPool> mStateMachineThreadPool; - - // Timer to run the state machine cycles. Used by - // ScheduleStateMachine(). Access protected by decoder monitor. - nsCOMPtr<nsITimer> mTimer; - - // Timestamp at which the next state machine cycle will run. - // Access protected by decoder monitor. - TimeStamp mTimeout; - - // Used to check if there are state machine cycles are running in sequence. - DebugOnly<bool> mInRunningStateMachine; - // The time that playback started from the system clock. This is used for // timing the presentation of video frames when there's no audio. // Accessed only via the state machine thread. Must be set via SetPlayStartTime. TimeStamp mPlayStartTime; // When we start writing decoded data to a new DecodedDataStream, or we // restart writing due to PlaybackStarted(), we record where we are in the // MediaStream and what that corresponds to in the media. @@ -906,19 +896,16 @@ protected: // memory and CPU overhead. bool mMinimizePreroll; // True if the decode thread has gone filled its buffers and is now // waiting to be awakened before it continues decoding. Synchronized // by the decoder monitor. bool mDecodeThreadWaiting; - // True is we are decoding a realtime stream, like a camera stream - bool mRealTime; - // True if we've dispatched a task to the decode task queue to call // ReadMetadata on the reader. We maintain a flag to ensure that we don't // dispatch multiple tasks to re-do the metadata loading. bool mDispatchedDecodeMetadataTask; // These two flags are true when we need to drop decoded samples that // we receive up to the next discontinuity. We do this when we seek; // the first sample in each stream after the seek is marked as being @@ -937,15 +924,12 @@ protected: // Stores presentation info required for playback. The decoder monitor // must be held when accessing this. MediaInfo mInfo; mozilla::MediaMetadataManager mMetadataManager; MediaDecoderOwner::NextFrameStatus mLastFrameStatus; - - // The id of timer tasks, used to ignore tasks that are scheduled previously. - int mTimerId; }; } // namespace mozilla; #endif
new file mode 100644 --- /dev/null +++ b/content/media/MediaDecoderStateMachineScheduler.cpp @@ -0,0 +1,233 @@ +/* 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 "MediaDecoderStateMachineScheduler.h" +#include "SharedThreadPool.h" +#include "mozilla/Preferences.h" +#include "mozilla/ReentrantMonitor.h" +#include "nsITimer.h" +#include "nsComponentManagerUtils.h" +#include "VideoUtils.h" + +namespace { +class TimerEvent : public nsITimerCallback, public nsRunnable { + typedef mozilla::MediaDecoderStateMachineScheduler Scheduler; + NS_DECL_THREADSAFE_ISUPPORTS +public: + TimerEvent(Scheduler* aScheduler, int aTimerId) + : mScheduler(aScheduler), mTimerId(aTimerId) {} + + NS_IMETHOD Run() MOZ_OVERRIDE { + return mScheduler->TimeoutExpired(mTimerId); + } + + NS_IMETHOD Notify(nsITimer* aTimer) MOZ_OVERRIDE { + return mScheduler->TimeoutExpired(mTimerId); + } +private: + ~TimerEvent() {} + Scheduler* const mScheduler; + const int mTimerId; +}; + +NS_IMPL_ISUPPORTS(TimerEvent, nsITimerCallback, nsIRunnable); +} // anonymous namespace + +static already_AddRefed<nsIEventTarget> +CreateStateMachineThread() +{ + using mozilla::SharedThreadPool; + using mozilla::RefPtr; + RefPtr<SharedThreadPool> threadPool( + SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1)); + nsCOMPtr<nsIEventTarget> rv = threadPool.get(); + return rv.forget(); +} + +namespace mozilla { + +MediaDecoderStateMachineScheduler::MediaDecoderStateMachineScheduler( + ReentrantMonitor& aMonitor, + nsresult (*aTimeoutCallback)(void*), + void* aClosure, bool aRealTime) + : mTimeoutCallback(aTimeoutCallback) + , mClosure(aClosure) + // Only enable realtime mode when "media.realtime_decoder.enabled" is true. + , mRealTime(aRealTime && + Preferences::GetBool("media.realtime_decoder.enabled", false)) + , mMonitor(aMonitor) + , mEventTarget(CreateStateMachineThread()) + , mTimer(do_CreateInstance("@mozilla.org/timer;1")) + , mTimerId(0) + , mState(SCHEDULER_STATE_NONE) + , mInRunningStateMachine(false) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_COUNT_CTOR(MediaDecoderStateMachineScheduler); +} + +MediaDecoderStateMachineScheduler::~MediaDecoderStateMachineScheduler() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_COUNT_DTOR(MediaDecoderStateMachineScheduler); +} + +nsresult +MediaDecoderStateMachineScheduler::Init() +{ + MOZ_ASSERT(NS_IsMainThread()); + NS_ENSURE_TRUE(mEventTarget, NS_ERROR_FAILURE); + nsresult rv = mTimer->SetTarget(mEventTarget); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +nsresult +MediaDecoderStateMachineScheduler::Schedule(int64_t aUsecs) +{ + mMonitor.AssertCurrentThreadIn(); + + switch(mState) { + case SCHEDULER_STATE_SHUTDOWN: + return NS_ERROR_FAILURE; + case SCHEDULER_STATE_FROZEN: + mState = SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK; + case SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK: + return NS_OK; + case SCHEDULER_STATE_NONE: + break; + } + + aUsecs = std::max<int64_t>(aUsecs, 0); + + TimeStamp timeout = TimeStamp::Now() + + TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS); + + if (!mTimeout.IsNull() && timeout >= mTimeout) { + // We've already scheduled a timer set to expire at or before this time, + // or have an event dispatched to run the state machine. + return NS_OK; + } + + uint32_t ms = static_cast<uint32_t>((aUsecs / USECS_PER_MS) & 0xFFFFFFFF); + if (IsRealTime() && ms > 40) { + ms = 40; + } + + // Don't cancel the timer here for this function will be called from + // different threads. + + nsresult rv = NS_ERROR_FAILURE; + nsRefPtr<TimerEvent> event = new TimerEvent(this, mTimerId+1); + + if (ms == 0) { + // Dispatch a runnable to the state machine thread when delay is 0. + // It will has less latency than dispatching a runnable to the state + // machine thread which will then schedule a zero-delay timer. + rv = mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL); + } else if (OnStateMachineThread()) { + rv = mTimer->InitWithCallback(event, ms, nsITimer::TYPE_ONE_SHOT); + } else { + MOZ_ASSERT(false, "non-zero delay timer should be only " + "scheduled in state machine thread"); + } + + if (NS_SUCCEEDED(rv)) { + mTimeout = timeout; + ++mTimerId; + } else { + NS_WARNING("Failed to schedule state machine"); + } + + return rv; +} + +nsresult +MediaDecoderStateMachineScheduler::TimeoutExpired(int aTimerId) +{ + ReentrantMonitorAutoEnter mon(mMonitor); + MOZ_ASSERT(OnStateMachineThread()); + MOZ_ASSERT(!mInRunningStateMachine, + "State machine cycles must run in sequence!"); + + mInRunningStateMachine = true; + // Only run state machine cycles when id matches. + nsresult rv = NS_OK; + if (mTimerId == aTimerId) { + ResetTimer(); + rv = mTimeoutCallback(mClosure); + } + mInRunningStateMachine = false; + + return rv; +} + +void +MediaDecoderStateMachineScheduler::ScheduleAndShutdown() +{ + mMonitor.AssertCurrentThreadIn(); + if (IsFrozen()) { + ThawScheduling(); + } + // Schedule next cycle to handle SHUTDOWN in state machine thread. + Schedule(); + // This must be set after calling Schedule() + // which does nothing in shutdown state. + mState = SCHEDULER_STATE_SHUTDOWN; +} + +bool +MediaDecoderStateMachineScheduler::OnStateMachineThread() const +{ + bool rv = false; + mEventTarget->IsOnCurrentThread(&rv); + return rv; +} + +bool +MediaDecoderStateMachineScheduler::IsScheduled() const +{ + mMonitor.AssertCurrentThreadIn(); + return !mTimeout.IsNull(); +} + +void +MediaDecoderStateMachineScheduler::ResetTimer() +{ + mMonitor.AssertCurrentThreadIn(); + mTimer->Cancel(); + mTimeout = TimeStamp(); +} + +void MediaDecoderStateMachineScheduler::FreezeScheduling() +{ + mMonitor.AssertCurrentThreadIn(); + if (mState == SCHEDULER_STATE_SHUTDOWN) { + return; + } + MOZ_ASSERT(mState == SCHEDULER_STATE_NONE); + mState = !IsScheduled() ? SCHEDULER_STATE_FROZEN : + SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK; + // Nullify pending timer task if any. + ++mTimerId; + mTimeout = TimeStamp(); +} + +void MediaDecoderStateMachineScheduler::ThawScheduling() +{ + mMonitor.AssertCurrentThreadIn(); + if (mState == SCHEDULER_STATE_SHUTDOWN) { + return; + } + // We should be in frozen state and no pending timer task. + MOZ_ASSERT(IsFrozen() && !IsScheduled()); + bool pendingTask = mState == SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK; + mState = SCHEDULER_STATE_NONE; + if (pendingTask) { + Schedule(); + } +} + +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/content/media/MediaDecoderStateMachineScheduler.h @@ -0,0 +1,86 @@ +/* -*- 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/. */ + +#ifndef MediaDecoderStateMachineScheduler_h__ +#define MediaDecoderStateMachineScheduler_h__ + +#include "nsCOMPtr.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/DebugOnly.h" + +class nsITimer; +class nsIEventTarget; + +namespace mozilla { + +class ReentrantMonitor; + +class MediaDecoderStateMachineScheduler { + enum State { + SCHEDULER_STATE_NONE, + SCHEDULER_STATE_FROZEN, + SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK, + SCHEDULER_STATE_SHUTDOWN + }; +public: + MediaDecoderStateMachineScheduler(ReentrantMonitor& aMonitor, + nsresult (*aTimeoutCallback)(void*), + void* aClosure, bool aRealTime); + ~MediaDecoderStateMachineScheduler(); + nsresult Init(); + nsresult Schedule(int64_t aUsecs = 0); + void ScheduleAndShutdown(); + nsresult TimeoutExpired(int aTimerId); + void FreezeScheduling(); + void ThawScheduling(); + + bool OnStateMachineThread() const; + bool IsScheduled() const; + + bool IsRealTime() const { + return mRealTime; + } + + nsIEventTarget* GetStateMachineThread() const { + return mEventTarget; + } + + bool IsFrozen() const { + return mState == SCHEDULER_STATE_FROZEN || + mState == SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK; + } + +private: + void ResetTimer(); + + // Callback function provided by MediaDecoderStateMachine to run + // state machine cycles. + nsresult (*const mTimeoutCallback)(void*); + // Since StateMachineScheduler will never outlive the state machine, + // it is safe to keep a raw pointer only to avoid reference cycles. + void* const mClosure; + // True is we are decoding a realtime stream, like a camera stream + const bool mRealTime; + // Monitor of the decoder + ReentrantMonitor& mMonitor; + // State machine thread + const nsCOMPtr<nsIEventTarget> mEventTarget; + // Timer to schedule callbacks to run the state machine cycles. + nsCOMPtr<nsITimer> mTimer; + // Timestamp at which the next state machine cycle will run. + TimeStamp mTimeout; + // The id of timer tasks, timer callback will only run if id matches. + int mTimerId; + // No more state machine cycles in shutdown state. + State mState; + + // Used to check if state machine cycles are running in sequence. + DebugOnly<bool> mInRunningStateMachine; +}; + +} // namespace mozilla + +#endif // MediaDecoderStateMachineScheduler_h__
--- a/content/media/MediaResource.h +++ b/content/media/MediaResource.h @@ -142,16 +142,20 @@ public: } // Clears byte range values. void Clear() { mStart = 0; mEnd = 0; } + bool Contains(const MediaByteRange& aByteRange) const { + return aByteRange.mStart >= mStart && aByteRange.mEnd <= mEnd; + } + int64_t mStart, mEnd; }; // Represents a section of contiguous media, with a start and end offset, and // a timestamp representing the start time. class TimestampedMediaByteRange : public MediaByteRange { public: TimestampedMediaByteRange() : MediaByteRange(), mStartTime(-1) {}
--- a/content/media/fmp4/MP4Reader.cpp +++ b/content/media/fmp4/MP4Reader.cpp @@ -8,16 +8,17 @@ #include "MediaResource.h" #include "nsSize.h" #include "VideoUtils.h" #include "mozilla/dom/HTMLMediaElement.h" #include "ImageContainer.h" #include "Layers.h" #include "SharedThreadPool.h" #include "mozilla/Preferences.h" +#include "mozilla/dom/TimeRanges.h" using mozilla::layers::Image; using mozilla::layers::LayerManager; using mozilla::layers::LayersBackend; #ifdef PR_LOGGING PRLogModuleInfo* GetDemuxerLog() { static PRLogModuleInfo* log = nullptr; @@ -548,9 +549,28 @@ MP4Reader::Seek(int64_t aTime, if (mDemuxer->HasValidAudio()) { mDemuxer->SeekAudio( mQueuedVideoSample ? mQueuedVideoSample->composition_timestamp : aTime); } return NS_OK; } +nsresult +MP4Reader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) +{ + nsTArray<MediaByteRange> ranges; + if (NS_FAILED(mDecoder->GetResource()->GetCachedRanges(ranges))) { + return NS_ERROR_FAILURE; + } + + nsTArray<Interval<Microseconds>> timeRanges; + mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges); + + for (size_t i = 0; i < timeRanges.Length(); i++) { + aBuffered->Add((timeRanges[i].start - aStartTime) / 1000000.0, + (timeRanges[i].end - aStartTime) / 1000000.0); + } + + return NS_OK; +} + } // namespace mozilla
--- a/content/media/fmp4/MP4Reader.h +++ b/content/media/fmp4/MP4Reader.h @@ -47,16 +47,19 @@ public: virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime) MOZ_OVERRIDE; virtual bool IsMediaSeekable() MOZ_OVERRIDE; + virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, + int64_t aStartTime) MOZ_OVERRIDE; + private: // Destroys all decoder resources. void Shutdown(); // Initializes mLayersBackendType if possible. void InitLayersBackendType();
--- a/content/media/fmp4/ffmpeg/FFmpegAACDecoder.cpp +++ b/content/media/fmp4/ffmpeg/FFmpegAACDecoder.cpp @@ -66,16 +66,17 @@ void FFmpegAACDecoder<LIBAV_VER>::DecodePacket(MP4Sample* aSample) { nsAutoPtr<AVFrame> frame(avcodec_alloc_frame()); avcodec_get_frame_defaults(frame); AVPacket packet; av_init_packet(&packet); + aSample->Pad(FF_INPUT_BUFFER_PADDING_SIZE); packet.data = aSample->data; packet.size = aSample->size; packet.pos = aSample->byte_offset; int decoded; int bytesConsumed = avcodec_decode_audio4(&mCodecContext, frame.get(), &decoded, &packet);
--- a/content/media/fmp4/ffmpeg/FFmpegDataDecoder.cpp +++ b/content/media/fmp4/ffmpeg/FFmpegDataDecoder.cpp @@ -80,21 +80,24 @@ FFmpegDataDecoder<LIBAV_VER>::Init() // FFmpeg takes this as a suggestion for what format to use for audio samples. mCodecContext.request_sample_fmt = AV_SAMPLE_FMT_FLT; // FFmpeg will call back to this to negotiate a video pixel format. mCodecContext.get_format = ChoosePixelFormat; mCodecContext.thread_count = PR_GetNumberOfProcessors(); - mCodecContext.thread_type = FF_THREAD_FRAME; + mCodecContext.thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME; mCodecContext.thread_safe_callbacks = false; + mCodecContext.extradata_size = mExtraData.length(); + for (int i = 0; i < FF_INPUT_BUFFER_PADDING_SIZE; i++) { + mExtraData.append(0); + } mCodecContext.extradata = mExtraData.begin(); - mCodecContext.extradata_size = mExtraData.length(); AVDictionary* opts = nullptr; if (avcodec_open2(&mCodecContext, codec, &opts) < 0) { NS_WARNING("Couldn't initialise ffmpeg decoder"); return NS_ERROR_FAILURE; } if (mCodecContext.codec_type == AVMEDIA_TYPE_AUDIO &&
--- a/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp +++ b/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp @@ -48,16 +48,17 @@ FFmpegH264Decoder<LIBAV_VER>::Init() } void FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(mp4_demuxer::MP4Sample* aSample) { AVPacket packet; av_init_packet(&packet); + aSample->Pad(FF_INPUT_BUFFER_PADDING_SIZE); packet.data = aSample->data; packet.size = aSample->size; packet.pts = aSample->composition_timestamp; packet.flags = aSample->is_sync_point ? AV_PKT_FLAG_KEY : 0; packet.pos = aSample->byte_offset; nsAutoPtr<AVFrame> frame(avcodec_alloc_frame()); avcodec_get_frame_defaults(frame); @@ -132,17 +133,21 @@ FFmpegH264Decoder<LIBAV_VER>::AllocateBu return avcodec_default_get_buffer(aCodecContext, aFrame); } } /* static */ void FFmpegH264Decoder<LIBAV_VER>::ReleaseBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame) { - reinterpret_cast<Image*>(aFrame->opaque)->Release(); + Image* image = reinterpret_cast<Image*>(aFrame->opaque); + avcodec_default_release_buffer(aCodecContext, aFrame); + if (image) { + image->Release(); + } } int FFmpegH264Decoder<LIBAV_VER>::AllocateYUV420PVideoBuffer( AVCodecContext* aCodecContext, AVFrame* aFrame) { // Older versions of ffmpeg require that edges be allocated* around* the // actual image.
--- a/content/media/gmp/GMPMessageUtils.h +++ b/content/media/gmp/GMPMessageUtils.h @@ -2,20 +2,35 @@ /* 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 GMPMessageUtils_h_ #define GMPMessageUtils_h_ #include "gmp-video-codec.h" +#include "gmp-video-frame-encoded.h" namespace IPC { template <> +struct ParamTraits<GMPErr> +: public ContiguousEnumSerializer<GMPErr, + GMPNoErr, + GMPLastErr> +{}; + +template <> +struct ParamTraits<GMPVideoFrameType> +: public ContiguousEnumSerializer<GMPVideoFrameType, + kGMPKeyFrame, + kGMPSkipFrame> +{}; + +template <> struct ParamTraits<GMPVideoCodecComplexity> : public ContiguousEnumSerializer<GMPVideoCodecComplexity, kGMPComplexityNormal, kGMPComplexityInvalid> {}; template <> struct ParamTraits<GMPVP8ResilienceMode> @@ -34,67 +49,21 @@ struct ParamTraits<GMPVideoCodecType> template <> struct ParamTraits<GMPVideoCodecMode> : public ContiguousEnumSerializer<GMPVideoCodecMode, kGMPRealtimeVideo, kGMPCodecModeInvalid> {}; template <> -struct ParamTraits<GMPVideoCodecVP8> -{ - typedef GMPVideoCodecVP8 paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.mPictureLossIndicationOn); - WriteParam(aMsg, aParam.mFeedbackModeOn); - WriteParam(aMsg, aParam.mComplexity); - WriteParam(aMsg, aParam.mResilience); - WriteParam(aMsg, aParam.mNumberOfTemporalLayers); - WriteParam(aMsg, aParam.mDenoisingOn); - WriteParam(aMsg, aParam.mErrorConcealmentOn); - WriteParam(aMsg, aParam.mAutomaticResizeOn); - WriteParam(aMsg, aParam.mFrameDroppingOn); - WriteParam(aMsg, aParam.mKeyFrameInterval); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - if (ReadParam(aMsg, aIter, &(aResult->mPictureLossIndicationOn)) && - ReadParam(aMsg, aIter, &(aResult->mFeedbackModeOn)) && - ReadParam(aMsg, aIter, &(aResult->mComplexity)) && - ReadParam(aMsg, aIter, &(aResult->mResilience)) && - ReadParam(aMsg, aIter, &(aResult->mNumberOfTemporalLayers)) && - ReadParam(aMsg, aIter, &(aResult->mDenoisingOn)) && - ReadParam(aMsg, aIter, &(aResult->mErrorConcealmentOn)) && - ReadParam(aMsg, aIter, &(aResult->mAutomaticResizeOn)) && - ReadParam(aMsg, aIter, &(aResult->mFrameDroppingOn)) && - ReadParam(aMsg, aIter, &(aResult->mKeyFrameInterval))) { - return true; - } - - return false; - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - aLog->append(StringPrintf(L"[%d, %d, %d, %d, %u, %d, %d, %d, %d, %d]", - aParam.mPictureLossIndicationOn, - aParam.mFeedbackModeOn, - aParam.mComplexity, - aParam.mResilience, - aParam.mNumberOfTemporalLayers, - aParam.mDenoisingOn, - aParam.mErrorConcealmentOn, - aParam.mAutomaticResizeOn, - aParam.mFrameDroppingOn, - aParam.mKeyFrameInterval)); - } -}; +struct ParamTraits<GMPBufferType> +: public ContiguousEnumSerializer<GMPBufferType, + GMP_BufferSingle, + GMP_BufferInvalid> +{}; template <> struct ParamTraits<GMPSimulcastStream> { typedef GMPSimulcastStream paramType; static void Write(Message* aMsg, const paramType& aParam) { @@ -131,40 +100,43 @@ struct ParamTraits<GMPSimulcastStream> template <> struct ParamTraits<GMPVideoCodec> { typedef GMPVideoCodec paramType; static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mGMPApiVersion); WriteParam(aMsg, aParam.mCodecType); WriteParam(aMsg, nsAutoCString(aParam.mPLName)); WriteParam(aMsg, aParam.mPLType); WriteParam(aMsg, aParam.mWidth); WriteParam(aMsg, aParam.mHeight); WriteParam(aMsg, aParam.mStartBitrate); WriteParam(aMsg, aParam.mMaxBitrate); WriteParam(aMsg, aParam.mMinBitrate); WriteParam(aMsg, aParam.mMaxFramerate); - if (aParam.mCodecType == kGMPVideoCodecVP8) { - WriteParam(aMsg, aParam.mCodecSpecific.mVP8); - } else { - MOZ_ASSERT(false, "Serializing unknown codec type!"); - } + WriteParam(aMsg, aParam.mFrameDroppingOn); + WriteParam(aMsg, aParam.mKeyFrameInterval); WriteParam(aMsg, aParam.mQPMax); WriteParam(aMsg, aParam.mNumberOfSimulcastStreams); for (uint32_t i = 0; i < aParam.mNumberOfSimulcastStreams; i++) { WriteParam(aMsg, aParam.mSimulcastStream[i]); } WriteParam(aMsg, aParam.mMode); } static bool Read(const Message* aMsg, void** aIter, paramType* aResult) { + // NOTE: make sure this matches any versions supported + if (!ReadParam(aMsg, aIter, &(aResult->mGMPApiVersion)) || + aResult->mGMPApiVersion != kGMPVersion33) { + return false; + } if (!ReadParam(aMsg, aIter, &(aResult->mCodecType))) { return false; } nsAutoCString plName; if (!ReadParam(aMsg, aIter, &plName) || plName.Length() > kGMPPayloadNameSize - 1) { return false; @@ -173,26 +145,19 @@ struct ParamTraits<GMPVideoCodec> memset(aResult->mPLName + plName.Length(), 0, kGMPPayloadNameSize - plName.Length()); if (!ReadParam(aMsg, aIter, &(aResult->mPLType)) || !ReadParam(aMsg, aIter, &(aResult->mWidth)) || !ReadParam(aMsg, aIter, &(aResult->mHeight)) || !ReadParam(aMsg, aIter, &(aResult->mStartBitrate)) || !ReadParam(aMsg, aIter, &(aResult->mMaxBitrate)) || !ReadParam(aMsg, aIter, &(aResult->mMinBitrate)) || - !ReadParam(aMsg, aIter, &(aResult->mMaxFramerate))) { - return false; - } - - if (aResult->mCodecType == kGMPVideoCodecVP8) { - if (!ReadParam(aMsg, aIter, &(aResult->mCodecSpecific.mVP8))) { - return false; - } - } else { - MOZ_ASSERT(false, "De-serializing unknown codec type!"); + !ReadParam(aMsg, aIter, &(aResult->mMaxFramerate)) || + !ReadParam(aMsg, aIter, &(aResult->mFrameDroppingOn)) || + !ReadParam(aMsg, aIter, &(aResult->mKeyFrameInterval))) { return false; } if (!ReadParam(aMsg, aIter, &(aResult->mQPMax)) || !ReadParam(aMsg, aIter, &(aResult->mNumberOfSimulcastStreams))) { return false; } @@ -221,109 +186,11 @@ struct ParamTraits<GMPVideoCodec> } aLog->append(StringPrintf(L"[%s, %u, %u]", codecName, aParam.mWidth, aParam.mHeight)); } }; -template <> -struct ParamTraits<GMPCodecSpecificInfoVP8> -{ - typedef GMPCodecSpecificInfoVP8 paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.mHasReceivedSLI); - WriteParam(aMsg, aParam.mPictureIdSLI); - WriteParam(aMsg, aParam.mHasReceivedRPSI); - WriteParam(aMsg, aParam.mPictureIdRPSI); - WriteParam(aMsg, aParam.mPictureId); - WriteParam(aMsg, aParam.mNonReference); - WriteParam(aMsg, aParam.mSimulcastIdx); - WriteParam(aMsg, aParam.mTemporalIdx); - WriteParam(aMsg, aParam.mLayerSync); - WriteParam(aMsg, aParam.mTL0PicIdx); - WriteParam(aMsg, aParam.mKeyIdx); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - if (ReadParam(aMsg, aIter, &(aResult->mHasReceivedSLI)) && - ReadParam(aMsg, aIter, &(aResult->mPictureIdSLI)) && - ReadParam(aMsg, aIter, &(aResult->mHasReceivedRPSI)) && - ReadParam(aMsg, aIter, &(aResult->mPictureIdRPSI)) && - ReadParam(aMsg, aIter, &(aResult->mPictureId)) && - ReadParam(aMsg, aIter, &(aResult->mNonReference)) && - ReadParam(aMsg, aIter, &(aResult->mSimulcastIdx)) && - ReadParam(aMsg, aIter, &(aResult->mTemporalIdx)) && - ReadParam(aMsg, aIter, &(aResult->mLayerSync)) && - ReadParam(aMsg, aIter, &(aResult->mTL0PicIdx)) && - ReadParam(aMsg, aIter, &(aResult->mKeyIdx))) { - return true; - } - return false; - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - aLog->append(StringPrintf(L"[%d, %u, %d, %u, %d, %d, %u, %u, %d, %d, %d]", - aParam.mHasReceivedSLI, - aParam.mPictureIdSLI, - aParam.mHasReceivedRPSI, - aParam.mPictureIdRPSI, - aParam.mPictureId, - aParam.mNonReference, - aParam.mSimulcastIdx, - aParam.mTemporalIdx, - aParam.mLayerSync, - aParam.mTL0PicIdx, - aParam.mKeyIdx)); - } -}; - -template <> -struct ParamTraits<GMPCodecSpecificInfo> -{ - typedef GMPCodecSpecificInfo paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.mCodecType); - if (aParam.mCodecType == kGMPVideoCodecVP8) { - WriteParam(aMsg, aParam.mCodecSpecific.mVP8); - } else { - MOZ_ASSERT(false, "Serializing unknown codec type!"); - } - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - if (!ReadParam(aMsg, aIter, &(aResult->mCodecType))) { - return false; - } - - if (aResult->mCodecType == kGMPVideoCodecVP8) { - if (!ReadParam(aMsg, aIter, &(aResult->mCodecSpecific.mVP8))) { - return false; - } - } else { - MOZ_ASSERT(false, "De-serializing unknown codec type!"); - return false; - } - - return true; - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - const char* codecName = nullptr; - if (aParam.mCodecType == kGMPVideoCodecVP8) { - codecName = "VP8"; - } - aLog->append(StringPrintf(L"[%s]", codecName)); - } -}; - } // namespace IPC #endif // GMPMessageUtils_h_
--- a/content/media/gmp/GMPParent.h +++ b/content/media/gmp/GMPParent.h @@ -11,16 +11,17 @@ #include "GMPVideoEncoderParent.h" #include "mozilla/gmp/PGMPParent.h" #include "nsCOMPtr.h" #include "nscore.h" #include "nsISupports.h" #include "nsString.h" #include "nsTArray.h" #include "nsIFile.h" +#include "ThreadSafeRefcountingWithMainThreadDestruction.h" class nsILineInputStream; class nsIThread; namespace mozilla { namespace gmp { class GMPCapability @@ -33,17 +34,17 @@ public: enum GMPState { GMPStateNotLoaded, GMPStateLoaded }; class GMPParent MOZ_FINAL : public PGMPParent { public: - NS_INLINE_DECL_REFCOUNTING(GMPParent) + NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(GMPParent) GMPParent(); nsresult Init(nsIFile* aPluginDir); nsresult LoadProcess(); void MaybeUnloadProcess(); void UnloadProcess(); bool SupportsAPI(const nsCString& aAPI, const nsCString& aTag);
--- a/content/media/gmp/GMPPlatform.cpp +++ b/content/media/gmp/GMPPlatform.cpp @@ -22,25 +22,26 @@ public: : mTask(aTask) { MOZ_ASSERT(mTask); } void Run() { mTask->Run(); + mTask->Destroy(); mTask = nullptr; } private: ~Runnable() { } - nsAutoPtr<GMPTask> mTask; + GMPTask* mTask; }; class SyncRunnable MOZ_FINAL { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SyncRunnable) SyncRunnable(GMPTask* aTask, MessageLoop* aMessageLoop) @@ -66,29 +67,30 @@ public: while (!mDone) { lock.Wait(); } } void Run() { mTask->Run(); + mTask->Destroy(); mTask = nullptr; MonitorAutoLock lock(mMonitor); mDone = true; lock.Notify(); } private: ~SyncRunnable() { } bool mDone; - nsAutoPtr<GMPTask> mTask; + GMPTask* mTask; MessageLoop* mMessageLoop; Monitor mMonitor; }; GMPErr CreateThread(GMPThread** aThread) { if (!aThread) { @@ -103,17 +105,16 @@ CreateThread(GMPThread** aThread) GMPErr RunOnMainThread(GMPTask* aTask) { if (!aTask || !sMainLoop) { return GMPGenericErr; } nsRefPtr<Runnable> r = new Runnable(aTask); - sMainLoop->PostTask(FROM_HERE, NewRunnableMethod(r.get(), &Runnable::Run)); return GMPNoErr; } GMPErr SyncRunOnMainThread(GMPTask* aTask) { @@ -147,16 +148,19 @@ InitPlatformAPI(GMPPlatformAPI& aPlatfor sMainLoop = MessageLoop::current(); } aPlatformAPI.version = 0; aPlatformAPI.createthread = &CreateThread; aPlatformAPI.runonmainthread = &RunOnMainThread; aPlatformAPI.syncrunonmainthread = &SyncRunOnMainThread; aPlatformAPI.createmutex = &CreateMutex; + aPlatformAPI.createrecord = nullptr; + aPlatformAPI.settimer = nullptr; + aPlatformAPI.getcurrenttime = nullptr; } GMPThreadImpl::GMPThreadImpl() : mMutex("GMPThreadImpl"), mThread("GMPThread") { }
--- a/content/media/gmp/GMPPlatform.h +++ b/content/media/gmp/GMPPlatform.h @@ -8,16 +8,18 @@ #include "mozilla/Mutex.h" #include "gmp-platform.h" #include "base/thread.h" namespace mozilla { namespace gmp { +class GMPChild; + void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI); class GMPThreadImpl : public GMPThread { public: GMPThreadImpl(); virtual ~GMPThreadImpl();
--- a/content/media/gmp/GMPService.cpp +++ b/content/media/gmp/GMPService.cpp @@ -178,17 +178,17 @@ GeckoMediaPluginService::GetThread(nsITh return NS_OK; } NS_IMETHODIMP GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags, const nsAString& aOrigin, GMPVideoHost** aOutVideoHost, - GMPVideoDecoder** aGMPVD) + GMPVideoDecoderProxy** aGMPVD) { MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); NS_ENSURE_ARG(aTags && aTags->Length() > 0); NS_ENSURE_ARG(aOutVideoHost); NS_ENSURE_ARG(aGMPVD); if (mShuttingDownOnGMPThread) { return NS_ERROR_FAILURE; @@ -212,17 +212,17 @@ GeckoMediaPluginService::GetGMPVideoDeco return NS_OK; } NS_IMETHODIMP GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags, const nsAString& aOrigin, GMPVideoHost** aOutVideoHost, - GMPVideoEncoder** aGMPVE) + GMPVideoEncoderProxy** aGMPVE) { MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); NS_ENSURE_ARG(aTags && aTags->Length() > 0); NS_ENSURE_ARG(aOutVideoHost); NS_ENSURE_ARG(aGMPVE); if (mShuttingDownOnGMPThread) { return NS_ERROR_FAILURE; @@ -359,31 +359,51 @@ GeckoMediaPluginService::SelectPluginFor gmp->SetOrigin(aOrigin); } return gmp; } } return nullptr; } +class CreateGMPParentTask : public nsRunnable { +public: + NS_IMETHOD Run() { + MOZ_ASSERT(NS_IsMainThread()); + mParent = new GMPParent(); + return NS_OK; + } + already_AddRefed<GMPParent> GetParent() { + return mParent.forget(); + } +private: + nsRefPtr<GMPParent> mParent; +}; void GeckoMediaPluginService::AddOnGMPThread(const nsAString& aDirectory) { MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); nsCOMPtr<nsIFile> directory; nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory)); if (NS_WARN_IF(NS_FAILED(rv))) { return; } - nsRefPtr<GMPParent> gmp = new GMPParent(); + // The GMPParent inherits from IToplevelProtocol, which must be created + // on the main thread to be threadsafe. See Bug 1035653. + nsRefPtr<CreateGMPParentTask> task(new CreateGMPParentTask()); + nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); + MOZ_ASSERT(mainThread); + mozilla::SyncRunnable::DispatchToThread(mainThread, task); + nsRefPtr<GMPParent> gmp = task->GetParent(); rv = gmp->Init(directory); if (NS_FAILED(rv)) { + NS_WARNING("Can't Create GMPParent"); return; } mPlugins.AppendElement(gmp); } void GeckoMediaPluginService::RemoveOnGMPThread(const nsAString& aDirectory)
--- a/content/media/gmp/GMPService.h +++ b/content/media/gmp/GMPService.h @@ -1,16 +1,17 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef GMPService_h_ #define GMPService_h_ +#include "nsString.h" #include "mozIGeckoMediaPluginService.h" #include "nsIObserver.h" #include "nsTArray.h" #include "mozilla/Mutex.h" #include "nsString.h" #include "nsCOMPtr.h" #include "nsIThread.h" #include "nsThreadUtils.h"
new file mode 100644 --- /dev/null +++ b/content/media/gmp/GMPSharedMemManager.cpp @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GMPSharedMemManager.h" +#include "GMPMessageUtils.h" +#include "mozilla/ipc/SharedMemory.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/ClearOnShutdown.h" + +namespace mozilla { +namespace gmp { + +// Really one set of pools on each side of the plugin API. + +// YUV buffers go from Encoder parent to child; pool there, and then return +// with Decoded() frames to the Decoder parent and goes into the parent pool. +// Compressed (encoded) data goes from the Decoder parent to the child; +// pool there, and then return with Encoded() frames and goes into the parent +// pool. +static StaticAutoPtr<nsTArray<ipc::Shmem>> sGmpFreelist[GMPSharedMemManager::kGMPNumTypes]; +static uint32_t sGMPShmemManagerCount = 0; + +GMPSharedMemManager::GMPSharedMemManager() +{ + if (!sGMPShmemManagerCount) { + for (uint32_t i = 0; i < GMPSharedMemManager::kGMPNumTypes; i++) { + sGmpFreelist[i] = new nsTArray<ipc::Shmem>(); + } + } + sGMPShmemManagerCount++; +} + +GMPSharedMemManager::~GMPSharedMemManager() +{ + MOZ_ASSERT(sGMPShmemManagerCount > 0); + sGMPShmemManagerCount--; + if (!sGMPShmemManagerCount) { + for (uint32_t i = 0; i < GMPSharedMemManager::kGMPNumTypes; i++) { + sGmpFreelist[i] = nullptr; + } + } +} + +static nsTArray<ipc::Shmem>& +GetGmpFreelist(GMPSharedMemManager::GMPMemoryClasses aTypes) +{ + return *(sGmpFreelist[aTypes]); +} + +static uint32_t sGmpAllocated[GMPSharedMemManager::kGMPNumTypes]; // 0's + +bool +GMPSharedMemManager::MgrAllocShmem(GMPMemoryClasses aClass, size_t aSize, + ipc::Shmem::SharedMemory::SharedMemoryType aType, + ipc::Shmem* aMem) +{ + CheckThread(); + + // first look to see if we have a free buffer large enough + for (uint32_t i = 0; i < GetGmpFreelist(aClass).Length(); i++) { + MOZ_ASSERT(GetGmpFreelist(aClass)[i].IsWritable()); + if (aSize <= GetGmpFreelist(aClass)[i].Size<uint8_t>()) { + *aMem = GetGmpFreelist(aClass)[i]; + GetGmpFreelist(aClass).RemoveElementAt(i); + return true; + } + } + + // Didn't find a buffer free with enough space; allocate one + size_t pagesize = ipc::SharedMemory::SystemPageSize(); + aSize = (aSize + (pagesize-1)) & ~(pagesize-1); // round up to page size + bool retval = Alloc(aSize, aType, aMem); + if (retval) { + sGmpAllocated[aClass]++; + } + return retval; +} + +bool +GMPSharedMemManager::MgrDeallocShmem(GMPMemoryClasses aClass, ipc::Shmem& aMem) +{ + CheckThread(); + + size_t size = aMem.Size<uint8_t>(); + size_t total = 0; + // XXX This works; there are better pool algorithms. We need to avoid + // "falling off a cliff" with too low a number + if (GetGmpFreelist(aClass).Length() > 10) { + Dealloc(GetGmpFreelist(aClass)[0]); + GetGmpFreelist(aClass).RemoveElementAt(0); + // The allocation numbers will be fubar on the Child! + sGmpAllocated[aClass]--; + } + for (uint32_t i = 0; i < GetGmpFreelist(aClass).Length(); i++) { + MOZ_ASSERT(GetGmpFreelist(aClass)[i].IsWritable()); + total += GetGmpFreelist(aClass)[i].Size<uint8_t>(); + if (size < GetGmpFreelist(aClass)[i].Size<uint8_t>()) { + GetGmpFreelist(aClass).InsertElementAt(i, aMem); + return true; + } + } + GetGmpFreelist(aClass).AppendElement(aMem); + + return true; +} + +uint32_t +GMPSharedMemManager::NumInUse(GMPMemoryClasses aClass) +{ + return sGmpAllocated[aClass] - GetGmpFreelist(aClass).Length(); +} + +} +}
--- a/content/media/gmp/GMPSharedMemManager.h +++ b/content/media/gmp/GMPSharedMemManager.h @@ -2,25 +2,53 @@ /* 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 GMPSharedMemManager_h_ #define GMPSharedMemManager_h_ #include "mozilla/ipc/Shmem.h" +#include "nsTArray.h" namespace mozilla { namespace gmp { class GMPSharedMemManager { public: - virtual bool MgrAllocShmem(size_t aSize, + typedef enum { + kGMPFrameData = 0, + kGMPEncodedData, + kGMPNumTypes + } GMPMemoryClasses; + + // This is a heuristic - max of 10 free in the Child pool, plus those + // in-use for the encoder and decoder at the given moment and not yet + // returned to the parent pool (which is not included). If more than + // this are needed, we presume the client has either crashed or hung + // (perhaps temporarily). + static const uint32_t kGMPBufLimit = 20; + + GMPSharedMemManager(); + virtual ~GMPSharedMemManager(); + + virtual bool MgrAllocShmem(GMPMemoryClasses aClass, size_t aSize, ipc::Shmem::SharedMemory::SharedMemoryType aType, - ipc::Shmem* aMem) = 0; - virtual bool MgrDeallocShmem(ipc::Shmem& aMem) = 0; + ipc::Shmem* aMem); + virtual bool MgrDeallocShmem(GMPMemoryClasses aClass, ipc::Shmem& aMem); + + // So we can know if data is "piling up" for the plugin - I.e. it's hung or crashed + virtual uint32_t NumInUse(GMPMemoryClasses aClass); + + // Parent and child impls will differ here + virtual void CheckThread() = 0; + + // These have to be implemented using the AllocShmem/etc provided by the IPDL-generated interfaces, + // so have the Parent/Child implement them. + virtual bool Alloc(size_t aSize, ipc::Shmem::SharedMemory::SharedMemoryType aType, ipc::Shmem* aMem) = 0; + virtual void Dealloc(ipc::Shmem& aMem) = 0; }; } // namespace gmp } // namespace mozilla #endif // GMPSharedMemManager_h_
--- a/content/media/gmp/GMPTypes.ipdlh +++ b/content/media/gmp/GMPTypes.ipdlh @@ -1,24 +1,27 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using GMPBufferType from "gmp-video-codec.h"; + namespace mozilla { namespace gmp { struct GMPVideoEncodedFrameData { - int64_t mCaptureTime_ms; uint32_t mEncodedWidth; uint32_t mEncodedHeight; - uint32_t mTimeStamp; + uint64_t mTimestamp; // microseconds + uint64_t mDuration; // microseconds uint32_t mFrameType; uint32_t mSize; + GMPBufferType mBufferType; Shmem mBuffer; bool mCompleteFrame; }; struct GMPPlaneData { int32_t mSize; int32_t mStride; @@ -27,14 +30,14 @@ struct GMPPlaneData struct GMPVideoi420FrameData { GMPPlaneData mYPlane; GMPPlaneData mUPlane; GMPPlaneData mVPlane; int32_t mWidth; int32_t mHeight; - uint32_t mTimestamp; - int64_t mRenderTime_ms; + uint64_t mTimestamp; // microseconds + uint64_t mDuration; // microseconds }; } }
--- a/content/media/gmp/GMPVideoDecoderChild.cpp +++ b/content/media/gmp/GMPVideoDecoderChild.cpp @@ -75,67 +75,89 @@ GMPVideoDecoderChild::ReceivedDecodedFra void GMPVideoDecoderChild::InputDataExhausted() { MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); SendInputDataExhausted(); } -bool -GMPVideoDecoderChild::MgrAllocShmem(size_t aSize, - ipc::Shmem::SharedMemory::SharedMemoryType aType, - ipc::Shmem* aMem) +void +GMPVideoDecoderChild::DrainComplete() { MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); - return AllocShmem(aSize, aType, aMem); + SendDrainComplete(); } -bool -GMPVideoDecoderChild::MgrDeallocShmem(Shmem& aMem) +void +GMPVideoDecoderChild::ResetComplete() { MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); - return DeallocShmem(aMem); + SendResetComplete(); +} + +void +GMPVideoDecoderChild::CheckThread() +{ + MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); } bool GMPVideoDecoderChild::RecvInitDecode(const GMPVideoCodec& aCodecSettings, + const nsTArray<uint8_t>& aCodecSpecific, const int32_t& aCoreCount) { if (!mVideoDecoder) { return false; } // Ignore any return code. It is OK for this to fail without killing the process. - mVideoDecoder->InitDecode(aCodecSettings, this, aCoreCount); - + mVideoDecoder->InitDecode(aCodecSettings, + aCodecSpecific.Elements(), + aCodecSpecific.Length(), + this, + aCoreCount); return true; } bool GMPVideoDecoderChild::RecvDecode(const GMPVideoEncodedFrameData& aInputFrame, const bool& aMissingFrames, - const GMPCodecSpecificInfo& aCodecSpecificInfo, + const nsTArray<uint8_t>& aCodecSpecificInfo, const int64_t& aRenderTimeMs) { if (!mVideoDecoder) { return false; } auto f = new GMPVideoEncodedFrameImpl(aInputFrame, &mVideoHost); // Ignore any return code. It is OK for this to fail without killing the process. - mVideoDecoder->Decode(f, aMissingFrames, aCodecSpecificInfo, aRenderTimeMs); + mVideoDecoder->Decode(f, + aMissingFrames, + aCodecSpecificInfo.Elements(), + aCodecSpecificInfo.Length(), + aRenderTimeMs); return true; } bool +GMPVideoDecoderChild::RecvChildShmemForPool(Shmem& aFrameBuffer) +{ + if (aFrameBuffer.IsWritable()) { + mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPFrameData, + aFrameBuffer); + } + return true; +} + +bool GMPVideoDecoderChild::RecvReset() { if (!mVideoDecoder) { return false; } // Ignore any return code. It is OK for this to fail without killing the process. mVideoDecoder->Reset();
--- a/content/media/gmp/GMPVideoDecoderChild.h +++ b/content/media/gmp/GMPVideoDecoderChild.h @@ -6,53 +6,75 @@ #ifndef GMPVideoDecoderChild_h_ #define GMPVideoDecoderChild_h_ #include "nsString.h" #include "mozilla/gmp/PGMPVideoDecoderChild.h" #include "gmp-video-decode.h" #include "GMPSharedMemManager.h" #include "GMPVideoHost.h" +#include "mozilla/gmp/GMPTypes.h" namespace mozilla { namespace gmp { class GMPChild; class GMPVideoDecoderChild : public PGMPVideoDecoderChild, - public GMPDecoderCallback, + public GMPVideoDecoderCallback, public GMPSharedMemManager { public: GMPVideoDecoderChild(GMPChild* aPlugin); virtual ~GMPVideoDecoderChild(); void Init(GMPVideoDecoder* aDecoder); GMPVideoHostImpl& Host(); - // GMPDecoderCallback + // GMPVideoDecoderCallback virtual void Decoded(GMPVideoi420Frame* decodedFrame) MOZ_OVERRIDE; virtual void ReceivedDecodedReferenceFrame(const uint64_t pictureId) MOZ_OVERRIDE; virtual void ReceivedDecodedFrame(const uint64_t pictureId) MOZ_OVERRIDE; virtual void InputDataExhausted() MOZ_OVERRIDE; + virtual void DrainComplete() MOZ_OVERRIDE; + virtual void ResetComplete() MOZ_OVERRIDE; // GMPSharedMemManager - virtual bool MgrAllocShmem(size_t aSize, - ipc::Shmem::SharedMemory::SharedMemoryType aType, - ipc::Shmem* aMem) MOZ_OVERRIDE; - virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE; + virtual void CheckThread(); + virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem) + { +#ifndef SHMEM_ALLOC_IN_CHILD + return CallNeedShmem(aSize, aMem); +#else +#ifdef GMP_SAFE_SHMEM + return AllocShmem(aSize, aType, aMem); +#else + return AllocUnsafeShmem(aSize, aType, aMem); +#endif +#endif + } + virtual void Dealloc(Shmem& aMem) + { +#ifndef SHMEM_ALLOC_IN_CHILD + SendParentShmemForPool(aMem); +#else + DeallocShmem(aMem); +#endif + } private: // PGMPVideoDecoderChild - virtual bool RecvInitDecode(const GMPVideoCodec& codecSettings, - const int32_t& coreCount) MOZ_OVERRIDE; - virtual bool RecvDecode(const GMPVideoEncodedFrameData& inputFrame, - const bool& missingFrames, - const GMPCodecSpecificInfo& codecSpecificInfo, - const int64_t& renderTimeMs) MOZ_OVERRIDE; + virtual bool RecvInitDecode(const GMPVideoCodec& aCodecSettings, + const nsTArray<uint8_t>& aCodecSpecific, + const int32_t& aCoreCount) MOZ_OVERRIDE; + virtual bool RecvDecode(const GMPVideoEncodedFrameData& aInputFrame, + const bool& aMissingFrames, + const nsTArray<uint8_t>& aCodecSpecificInfo, + const int64_t& aRenderTimeMs) MOZ_OVERRIDE; + virtual bool RecvChildShmemForPool(Shmem& aFrameBuffer) MOZ_OVERRIDE; virtual bool RecvReset() MOZ_OVERRIDE; virtual bool RecvDrain() MOZ_OVERRIDE; virtual bool RecvDecodingComplete() MOZ_OVERRIDE; GMPChild* mPlugin; GMPVideoDecoder* mVideoDecoder; GMPVideoHostImpl mVideoHost; };
--- a/content/media/gmp/GMPVideoDecoderParent.cpp +++ b/content/media/gmp/GMPVideoDecoderParent.cpp @@ -7,16 +7,17 @@ #include "GMPVideoEncodedFrameImpl.h" #include "GMPVideoi420FrameImpl.h" #include "GMPParent.h" #include <stdio.h> #include "mozilla/unused.h" #include "GMPMessageUtils.h" #include "nsAutoRef.h" #include "nsThreadUtils.h" +#include "mozilla/gmp/GMPTypes.h" template <> class nsAutoRefTraits<GMPVideoEncodedFrame> : public nsPointerRefTraits<GMPVideoEncodedFrame> { public: static void Release(GMPVideoEncodedFrame* aFrame) { aFrame->Destroy(); } }; @@ -37,160 +38,159 @@ GMPVideoDecoderParent::~GMPVideoDecoderP } GMPVideoHostImpl& GMPVideoDecoderParent::Host() { return mVideoHost; } -bool -GMPVideoDecoderParent::MgrAllocShmem(size_t aSize, - ipc::Shmem::SharedMemory::SharedMemoryType aType, - ipc::Shmem* aMem) -{ - MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); - - return AllocShmem(aSize, aType, aMem); -} - -bool -GMPVideoDecoderParent::MgrDeallocShmem(Shmem& aMem) -{ - MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); - - return DeallocShmem(aMem); -} - -GMPVideoErr +nsresult GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings, - GMPDecoderCallback* aCallback, + const nsTArray<uint8_t>& aCodecSpecific, + GMPVideoDecoderCallback* aCallback, int32_t aCoreCount) { if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video decoder!"); - return GMPVideoGenericErr; + return NS_ERROR_FAILURE; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!aCallback) { - return GMPVideoGenericErr; + return NS_ERROR_FAILURE; } mCallback = aCallback; - if (!SendInitDecode(aCodecSettings, aCoreCount)) { - return GMPVideoGenericErr; + if (!SendInitDecode(aCodecSettings, aCodecSpecific, aCoreCount)) { + return NS_ERROR_FAILURE; } // Async IPC, we don't have access to a return value. - return GMPVideoNoErr; + return NS_OK; } -GMPVideoErr +nsresult GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame, bool aMissingFrames, - const GMPCodecSpecificInfo& aCodecSpecificInfo, + const nsTArray<uint8_t>& aCodecSpecificInfo, int64_t aRenderTimeMs) { nsAutoRef<GMPVideoEncodedFrame> autoDestroy(aInputFrame); if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video decoder!"); - return GMPVideoGenericErr; + return NS_ERROR_FAILURE; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); auto inputFrameImpl = static_cast<GMPVideoEncodedFrameImpl*>(aInputFrame); GMPVideoEncodedFrameData frameData; inputFrameImpl->RelinquishFrameData(frameData); + // Very rough kill-switch if the plugin stops processing. If it's merely + // hung and continues, we'll come back to life eventually. + // 3* is because we're using 3 buffers per frame for i420 data for now. + if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit || + NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) { + return NS_ERROR_FAILURE; + } + if (!SendDecode(frameData, aMissingFrames, aCodecSpecificInfo, aRenderTimeMs)) { - return GMPVideoGenericErr; + return NS_ERROR_FAILURE; } // Async IPC, we don't have access to a return value. - return GMPVideoNoErr; + return NS_OK; } -GMPVideoErr +nsresult GMPVideoDecoderParent::Reset() { if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video decoder!"); - return GMPVideoGenericErr; + return NS_ERROR_FAILURE; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendReset()) { - return GMPVideoGenericErr; + return NS_ERROR_FAILURE; } // Async IPC, we don't have access to a return value. - return GMPVideoNoErr; + return NS_OK; } -GMPVideoErr +nsresult GMPVideoDecoderParent::Drain() { if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video decoder!"); - return GMPVideoGenericErr; + return NS_ERROR_FAILURE; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendDrain()) { - return GMPVideoGenericErr; + return NS_ERROR_FAILURE; } // Async IPC, we don't have access to a return value. - return GMPVideoNoErr; + return NS_OK; } // Note: Consider keeping ActorDestroy sync'd up when making changes here. -void +nsresult GMPVideoDecoderParent::DecodingComplete() { if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video decoder!"); - return; + return NS_ERROR_FAILURE; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); mCanSendMessages = false; mCallback = nullptr; mVideoHost.DoneWithAPI(); unused << SendDecodingComplete(); + + return NS_OK; } // Note: Keep this sync'd up with DecodingComplete void GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) { if (mPlugin) { // Ignore any return code. It is OK for this to fail without killing the process. mPlugin->VideoDecoderDestroyed(this); mPlugin = nullptr; } mCanSendMessages = false; mCallback = nullptr; mVideoHost.ActorDestroyed(); } +void +GMPVideoDecoderParent::CheckThread() +{ + MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); +} + bool GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame) { if (!mCallback) { return false; } auto f = new GMPVideoi420FrameImpl(aDecodedFrame, &mVideoHost); @@ -236,16 +236,69 @@ GMPVideoDecoderParent::RecvInputDataExha // Ignore any return code. It is OK for this to fail without killing the process. mCallback->InputDataExhausted(); return true; } bool +GMPVideoDecoderParent::RecvDrainComplete() +{ + if (!mCallback) { + return false; + } + + // Ignore any return code. It is OK for this to fail without killing the process. + mCallback->DrainComplete(); + + return true; +} + +bool +GMPVideoDecoderParent::RecvResetComplete() +{ + if (!mCallback) { + return false; + } + + // Ignore any return code. It is OK for this to fail without killing the process. + mCallback->ResetComplete(); + + return true; +} + +bool +GMPVideoDecoderParent::RecvParentShmemForPool(Shmem& aEncodedBuffer) +{ + if (aEncodedBuffer.IsWritable()) { + mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPEncodedData, + aEncodedBuffer); + } + return true; +} + +bool +GMPVideoDecoderParent::AnswerNeedShmem(const uint32_t& aFrameBufferSize, + Shmem* aMem) +{ + ipc::Shmem mem; + + if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPFrameData, + aFrameBufferSize, + ipc::SharedMemory::TYPE_BASIC, &mem)) + { + return false; + } + *aMem = mem; + mem = ipc::Shmem(); + return true; +} + +bool GMPVideoDecoderParent::Recv__delete__() { if (mPlugin) { // Ignore any return code. It is OK for this to fail without killing the process. mPlugin->VideoDecoderDestroyed(this); mPlugin = nullptr; }
--- a/content/media/gmp/GMPVideoDecoderParent.h +++ b/content/media/gmp/GMPVideoDecoderParent.h @@ -7,64 +7,80 @@ #define GMPVideoDecoderParent_h_ #include "mozilla/RefPtr.h" #include "gmp-video-decode.h" #include "mozilla/gmp/PGMPVideoDecoderParent.h" #include "GMPMessageUtils.h" #include "GMPSharedMemManager.h" #include "GMPVideoHost.h" +#include "GMPVideoDecoderProxy.h" namespace mozilla { namespace gmp { class GMPParent; -class GMPVideoDecoderParent MOZ_FINAL : public GMPVideoDecoder - , public PGMPVideoDecoderParent +class GMPVideoDecoderParent MOZ_FINAL : public PGMPVideoDecoderParent , public GMPSharedMemManager + , public GMPVideoDecoderProxy { public: NS_INLINE_DECL_REFCOUNTING(GMPVideoDecoderParent) GMPVideoDecoderParent(GMPParent *aPlugin); GMPVideoHostImpl& Host(); - // GMPSharedMemManager - virtual bool MgrAllocShmem(size_t aSize, - ipc::Shmem::SharedMemory::SharedMemoryType aType, - ipc::Shmem* aMem) MOZ_OVERRIDE; - virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE; + // GMPVideoDecoder + virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings, + const nsTArray<uint8_t>& aCodecSpecific, + GMPVideoDecoderCallback* aCallback, + int32_t aCoreCount) MOZ_OVERRIDE; + virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame, + bool aMissingFrames, + const nsTArray<uint8_t>& aCodecSpecificInfo, + int64_t aRenderTimeMs = -1) MOZ_OVERRIDE; + virtual nsresult Reset() MOZ_OVERRIDE; + virtual nsresult Drain() MOZ_OVERRIDE; + virtual nsresult DecodingComplete() MOZ_OVERRIDE; - // GMPVideoDecoder - virtual GMPVideoErr InitDecode(const GMPVideoCodec& aCodecSettings, - GMPDecoderCallback* aCallback, - int32_t aCoreCount) MOZ_OVERRIDE; - virtual GMPVideoErr Decode(GMPVideoEncodedFrame* aInputFrame, - bool aMissingFrames, - const GMPCodecSpecificInfo& aCodecSpecificInfo, - int64_t aRenderTimeMs = -1) MOZ_OVERRIDE; - virtual GMPVideoErr Reset() MOZ_OVERRIDE; - virtual GMPVideoErr Drain() MOZ_OVERRIDE; - virtual void DecodingComplete() MOZ_OVERRIDE; + // GMPSharedMemManager + virtual void CheckThread(); + virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem) + { +#ifdef GMP_SAFE_SHMEM + return AllocShmem(aSize, aType, aMem); +#else + return AllocUnsafeShmem(aSize, aType, aMem); +#endif + } + virtual void Dealloc(Shmem& aMem) + { + DeallocShmem(aMem); + } private: ~GMPVideoDecoderParent(); // PGMPVideoDecoderParent virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; virtual bool RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame) MOZ_OVERRIDE; virtual bool RecvReceivedDecodedReferenceFrame(const uint64_t& aPictureId) MOZ_OVERRIDE; virtual bool RecvReceivedDecodedFrame(const uint64_t& aPictureId) MOZ_OVERRIDE; virtual bool RecvInputDataExhausted() MOZ_OVERRIDE; + virtual bool RecvDrainComplete() MOZ_OVERRIDE; + virtual bool RecvResetComplete() MOZ_OVERRIDE; + virtual bool RecvParentShmemForPool(Shmem& aEncodedBuffer) MOZ_OVERRIDE; + virtual bool AnswerNeedShmem(const uint32_t& aFrameBufferSize, + Shmem* aMem) MOZ_OVERRIDE; virtual bool Recv__delete__() MOZ_OVERRIDE; bool mCanSendMessages; GMPParent* mPlugin; - GMPDecoderCallback* mCallback; + GMPVideoDecoderCallback* mCallback; GMPVideoHostImpl mVideoHost; }; } // namespace gmp } // namespace mozilla #endif // GMPVideoDecoderParent_h_
new file mode 100644 --- /dev/null +++ b/content/media/gmp/GMPVideoDecoderProxy.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GMPVideoDecoderProxy_h_ +#define GMPVideoDecoderProxy_h_ + +#include "nsTArray.h" +#include "gmp-video-decode.h" +#include "gmp-video-frame-i420.h" +#include "gmp-video-frame-encoded.h" + +// A proxy to GMPVideoDecoder in the child process. +// GMPVideoDecoderParent exposes this to users the GMP. +// This enables Gecko to pass nsTArrays to the child GMP and avoid +// an extra copy when doing so. +class GMPVideoDecoderProxy { +public: + virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings, + const nsTArray<uint8_t>& aCodecSpecific, + GMPVideoDecoderCallback* aCallback, + int32_t aCoreCount) = 0; + virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame, + bool aMissingFrames, + const nsTArray<uint8_t>& aCodecSpecificInfo, + int64_t aRenderTimeMs = -1) = 0; + virtual nsresult Reset() = 0; + virtual nsresult Drain() = 0; + virtual nsresult DecodingComplete() = 0; +}; + +#endif
--- a/content/media/gmp/GMPVideoEncodedFrameImpl.cpp +++ b/content/media/gmp/GMPVideoEncodedFrameImpl.cpp @@ -9,51 +9,59 @@ #include "GMPSharedMemManager.h" namespace mozilla { namespace gmp { GMPVideoEncodedFrameImpl::GMPVideoEncodedFrameImpl(GMPVideoHostImpl* aHost) : mEncodedWidth(0), mEncodedHeight(0), - mTimeStamp(0), - mCaptureTime_ms(0), + mTimeStamp(0ll), + mDuration(0ll), mFrameType(kGMPDeltaFrame), mSize(0), mCompleteFrame(false), - mHost(aHost) + mHost(aHost), + mBufferType(GMP_BufferSingle) { MOZ_ASSERT(aHost); aHost->EncodedFrameCreated(this); } GMPVideoEncodedFrameImpl::GMPVideoEncodedFrameImpl(const GMPVideoEncodedFrameData& aFrameData, GMPVideoHostImpl* aHost) : mEncodedWidth(aFrameData.mEncodedWidth()), mEncodedHeight(aFrameData.mEncodedHeight()), - mTimeStamp(aFrameData.mTimeStamp()), - mCaptureTime_ms(aFrameData.mCaptureTime_ms()), + mTimeStamp(aFrameData.mTimestamp()), + mDuration(aFrameData.mDuration()), mFrameType(static_cast<GMPVideoFrameType>(aFrameData.mFrameType())), mSize(aFrameData.mSize()), mCompleteFrame(aFrameData.mCompleteFrame()), mHost(aHost), - mBuffer(aFrameData.mBuffer()) + mBuffer(aFrameData.mBuffer()), + mBufferType(aFrameData.mBufferType()) { MOZ_ASSERT(aHost); aHost->EncodedFrameCreated(this); } GMPVideoEncodedFrameImpl::~GMPVideoEncodedFrameImpl() { DestroyBuffer(); if (mHost) { mHost->EncodedFrameDestroyed(this); } } +const GMPEncryptedBufferData* +GMPVideoEncodedFrameImpl::GetDecryptionData() const +{ + return nullptr; +} + GMPVideoFrameFormat GMPVideoEncodedFrameImpl::GetFrameFormat() { return kGMPEncodedVideoFrame; } void GMPVideoEncodedFrameImpl::DoneWithAPI() @@ -75,79 +83,82 @@ GMPVideoEncodedFrameImpl::ActorDestroyed mHost = nullptr; } bool GMPVideoEncodedFrameImpl::RelinquishFrameData(GMPVideoEncodedFrameData& aFrameData) { aFrameData.mEncodedWidth() = mEncodedWidth; aFrameData.mEncodedHeight() = mEncodedHeight; - aFrameData.mTimeStamp() = mTimeStamp; - aFrameData.mCaptureTime_ms() = mCaptureTime_ms; + aFrameData.mTimestamp() = mTimeStamp; + aFrameData.mDuration() = mDuration; aFrameData.mFrameType() = mFrameType; aFrameData.mSize() = mSize; aFrameData.mCompleteFrame() = mCompleteFrame; aFrameData.mBuffer() = mBuffer; + aFrameData.mBufferType() = mBufferType; // This method is called right before Shmem is sent to another process. // We need to effectively zero out our member copy so that we don't // try to delete Shmem we don't own later. mBuffer = ipc::Shmem(); return true; } void GMPVideoEncodedFrameImpl::DestroyBuffer() { if (mHost && mBuffer.IsWritable()) { - mHost->SharedMemMgr()->MgrDeallocShmem(mBuffer); + mHost->SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPEncodedData, mBuffer); } mBuffer = ipc::Shmem(); } -GMPVideoErr +GMPErr GMPVideoEncodedFrameImpl::CreateEmptyFrame(uint32_t aSize) { - DestroyBuffer(); - - if (aSize != 0) { - if (!mHost->SharedMemMgr()->MgrAllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, &mBuffer) || + if (aSize == 0) { + DestroyBuffer(); + } else if (aSize > AllocatedSize()) { + DestroyBuffer(); + if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData, aSize, + ipc::SharedMemory::TYPE_BASIC, &mBuffer) || !Buffer()) { - return GMPVideoAllocErr; + return GMPAllocErr; } } - mSize = aSize; - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPVideoEncodedFrameImpl::CopyFrame(const GMPVideoEncodedFrame& aFrame) { auto& f = static_cast<const GMPVideoEncodedFrameImpl&>(aFrame); if (f.mSize != 0) { - GMPVideoErr err = CreateEmptyFrame(f.mSize); - if (err != GMPVideoNoErr) { + GMPErr err = CreateEmptyFrame(f.mSize); + if (err != GMPNoErr) { return err; } memcpy(Buffer(), f.Buffer(), f.mSize); } mEncodedWidth = f.mEncodedWidth; mEncodedHeight = f.mEncodedHeight; mTimeStamp = f.mTimeStamp; - mCaptureTime_ms = f.mCaptureTime_ms; + mDuration = f.mDuration; mFrameType = f.mFrameType; - mSize = f.mSize; + mSize = f.mSize; // already set... mCompleteFrame = f.mCompleteFrame; + mBufferType = f.mBufferType; // Don't copy host, that should have been set properly on object creation via host. - return GMPVideoNoErr; + return GMPNoErr; } void GMPVideoEncodedFrameImpl::SetEncodedWidth(uint32_t aEncodedWidth) { mEncodedWidth = aEncodedWidth; } @@ -165,37 +176,37 @@ GMPVideoEncodedFrameImpl::SetEncodedHeig uint32_t GMPVideoEncodedFrameImpl::EncodedHeight() { return mEncodedHeight; } void -GMPVideoEncodedFrameImpl::SetTimeStamp(uint32_t aTimeStamp) +GMPVideoEncodedFrameImpl::SetTimeStamp(uint64_t aTimeStamp) { mTimeStamp = aTimeStamp; } -uint32_t +uint64_t GMPVideoEncodedFrameImpl::TimeStamp() { return mTimeStamp; } void -GMPVideoEncodedFrameImpl::SetCaptureTime(int64_t aCaptureTime) +GMPVideoEncodedFrameImpl::SetDuration(uint64_t aDuration) { - mCaptureTime_ms = aCaptureTime; + mDuration = aDuration; } -int64_t -GMPVideoEncodedFrameImpl::CaptureTime() +uint64_t +GMPVideoEncodedFrameImpl::Duration() const { - return mCaptureTime_ms; + return mDuration; } void GMPVideoEncodedFrameImpl::SetFrameType(GMPVideoFrameType aFrameType) { mFrameType = aFrameType; } @@ -212,17 +223,18 @@ GMPVideoEncodedFrameImpl::SetAllocatedSi return; } if (!mHost) { return; } ipc::Shmem new_mem; - if (!mHost->SharedMemMgr()->MgrAllocShmem(aNewSize, ipc::SharedMemory::TYPE_BASIC, &new_mem) || + if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData, aNewSize, + ipc::SharedMemory::TYPE_BASIC, &new_mem) || !new_mem.get<uint8_t>()) { return; } if (mBuffer.IsReadable()) { memcpy(new_mem.get<uint8_t>(), Buffer(), mSize); } @@ -277,10 +289,22 @@ GMPVideoEncodedFrameImpl::Buffer() } void GMPVideoEncodedFrameImpl::Destroy() { delete this; } +GMPBufferType +GMPVideoEncodedFrameImpl::BufferType() const +{ + return mBufferType; +} + +void +GMPVideoEncodedFrameImpl::SetBufferType(GMPBufferType aBufferType) +{ + mBufferType = aBufferType; +} + } // namespace gmp } // namespace mozilla
--- a/content/media/gmp/GMPVideoEncodedFrameImpl.h +++ b/content/media/gmp/GMPVideoEncodedFrameImpl.h @@ -26,19 +26,20 @@ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GMPVideoEncodedFrameImpl_h_ #define GMPVideoEncodedFrameImpl_h_ -#include "gmp-video-errors.h" +#include "gmp-errors.h" #include "gmp-video-frame.h" #include "gmp-video-frame-encoded.h" +#include "gmp-decryption.h" #include "mozilla/ipc/Shmem.h" namespace mozilla { namespace gmp { class GMPVideoHostImpl; class GMPVideoEncodedFrameData; @@ -58,47 +59,56 @@ public: bool RelinquishFrameData(GMPVideoEncodedFrameData& aFrameData); // GMPVideoFrame virtual GMPVideoFrameFormat GetFrameFormat() MOZ_OVERRIDE; virtual void Destroy() MOZ_OVERRIDE; // GMPVideoEncodedFrame - virtual GMPVideoErr CreateEmptyFrame(uint32_t aSize) MOZ_OVERRIDE; - virtual GMPVideoErr CopyFrame(const GMPVideoEncodedFrame& aFrame) MOZ_OVERRIDE; + virtual GMPErr CreateEmptyFrame(uint32_t aSize) MOZ_OVERRIDE; + virtual GMPErr CopyFrame(const GMPVideoEncodedFrame& aFrame) MOZ_OVERRIDE; virtual void SetEncodedWidth(uint32_t aEncodedWidth) MOZ_OVERRIDE; virtual uint32_t EncodedWidth() MOZ_OVERRIDE; virtual void SetEncodedHeight(uint32_t aEncodedHeight) MOZ_OVERRIDE; virtual uint32_t EncodedHeight() MOZ_OVERRIDE; - virtual void SetTimeStamp(uint32_t aTimeStamp) MOZ_OVERRIDE; - virtual uint32_t TimeStamp() MOZ_OVERRIDE; - virtual void SetCaptureTime(int64_t aCaptureTime) MOZ_OVERRIDE; - virtual int64_t CaptureTime() MOZ_OVERRIDE; + // Microseconds + virtual void SetTimeStamp(uint64_t aTimeStamp) MOZ_OVERRIDE; + virtual uint64_t TimeStamp() MOZ_OVERRIDE; + // Set frame duration (microseconds) + // NOTE: next-frame's Timestamp() != this-frame's TimeStamp()+Duration() + // depending on rounding to avoid having to track roundoff errors + // and dropped/missing frames(!) (which may leave a large gap) + virtual void SetDuration(uint64_t aDuration) MOZ_OVERRIDE; + virtual uint64_t Duration() const MOZ_OVERRIDE; virtual void SetFrameType(GMPVideoFrameType aFrameType) MOZ_OVERRIDE; virtual GMPVideoFrameType FrameType() MOZ_OVERRIDE; virtual void SetAllocatedSize(uint32_t aNewSize) MOZ_OVERRIDE; virtual uint32_t AllocatedSize() MOZ_OVERRIDE; virtual void SetSize(uint32_t aSize) MOZ_OVERRIDE; virtual uint32_t Size() MOZ_OVERRIDE; virtual void SetCompleteFrame(bool aCompleteFrame) MOZ_OVERRIDE; virtual bool CompleteFrame() MOZ_OVERRIDE; virtual const uint8_t* Buffer() const MOZ_OVERRIDE; virtual uint8_t* Buffer() MOZ_OVERRIDE; + virtual GMPBufferType BufferType() const MOZ_OVERRIDE; + virtual void SetBufferType(GMPBufferType aBufferType) MOZ_OVERRIDE; + virtual const GMPEncryptedBufferData* GetDecryptionData() const MOZ_OVERRIDE; private: void DestroyBuffer(); uint32_t mEncodedWidth; uint32_t mEncodedHeight; - uint32_t mTimeStamp; - int64_t mCaptureTime_ms; + uint64_t mTimeStamp; + uint64_t mDuration; GMPVideoFrameType mFrameType; uint32_t mSize; bool mCompleteFrame; GMPVideoHostImpl* mHost; ipc::Shmem mBuffer; + GMPBufferType mBufferType; }; } // namespace gmp } // namespace mozilla #endif // GMPVideoEncodedFrameImpl_h_
--- a/content/media/gmp/GMPVideoEncoderChild.cpp +++ b/content/media/gmp/GMPVideoEncoderChild.cpp @@ -35,82 +35,88 @@ GMPVideoEncoderChild::Init(GMPVideoEncod GMPVideoHostImpl& GMPVideoEncoderChild::Host() { return mVideoHost; } void GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo) + const uint8_t* aCodecSpecificInfo, + uint32_t aCodecSpecificInfoLength) { MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); auto ef = static_cast<GMPVideoEncodedFrameImpl*>(aEncodedFrame); GMPVideoEncodedFrameData frameData; ef->RelinquishFrameData(frameData); - SendEncoded(frameData, aCodecSpecificInfo); + nsTArray<uint8_t> codecSpecific; + codecSpecific.AppendElements(aCodecSpecificInfo, aCodecSpecificInfoLength); + SendEncoded(frameData, codecSpecific); aEncodedFrame->Destroy(); } -bool -GMPVideoEncoderChild::MgrAllocShmem(size_t aSize, - ipc::Shmem::SharedMemory::SharedMemoryType aType, - ipc::Shmem* aMem) +void +GMPVideoEncoderChild::CheckThread() { MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); - - return AllocShmem(aSize, aType, aMem); -} - -bool -GMPVideoEncoderChild::MgrDeallocShmem(Shmem& aMem) -{ - MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); - - return DeallocShmem(aMem); } bool GMPVideoEncoderChild::RecvInitEncode(const GMPVideoCodec& aCodecSettings, + const nsTArray<uint8_t>& aCodecSpecific, const int32_t& aNumberOfCores, const uint32_t& aMaxPayloadSize) { if (!mVideoEncoder) { return false; } // Ignore any return code. It is OK for this to fail without killing the process. - mVideoEncoder->InitEncode(aCodecSettings, this, aNumberOfCores, aMaxPayloadSize); + mVideoEncoder->InitEncode(aCodecSettings, + aCodecSpecific.Elements(), + aCodecSpecific.Length(), + this, + aNumberOfCores, + aMaxPayloadSize); return true; } bool GMPVideoEncoderChild::RecvEncode(const GMPVideoi420FrameData& aInputFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo, - const InfallibleTArray<int>& aFrameTypes) + const nsTArray<uint8_t>& aCodecSpecificInfo, + const nsTArray<GMPVideoFrameType>& aFrameTypes) { if (!mVideoEncoder) { return false; } auto f = new GMPVideoi420FrameImpl(aInputFrame, &mVideoHost); - std::vector<GMPVideoFrameType> frameTypes(aFrameTypes.Length()); - for (uint32_t i = 0; i < aFrameTypes.Length(); i++) { - frameTypes[i] = static_cast<GMPVideoFrameType>(aFrameTypes[i]); + // Ignore any return code. It is OK for this to fail without killing the process. + mVideoEncoder->Encode(f, + aCodecSpecificInfo.Elements(), + aCodecSpecificInfo.Length(), + aFrameTypes.Elements(), + aFrameTypes.Length()); + + return true; +} + +bool +GMPVideoEncoderChild::RecvChildShmemForPool(Shmem& aEncodedBuffer) +{ + if (aEncodedBuffer.IsWritable()) { + mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPEncodedData, + aEncodedBuffer); } - - // Ignore any return code. It is OK for this to fail without killing the process. - mVideoEncoder->Encode(f, aCodecSpecificInfo, frameTypes); - return true; } bool GMPVideoEncoderChild::RecvSetChannelParameters(const uint32_t& aPacketLoss, const uint32_t& aRTT) { if (!mVideoEncoder) {
--- a/content/media/gmp/GMPVideoEncoderChild.h +++ b/content/media/gmp/GMPVideoEncoderChild.h @@ -13,44 +13,64 @@ #include "GMPVideoHost.h" namespace mozilla { namespace gmp { class GMPChild; class GMPVideoEncoderChild : public PGMPVideoEncoderChild, - public GMPEncoderCallback, + public GMPVideoEncoderCallback, public GMPSharedMemManager { public: GMPVideoEncoderChild(GMPChild* aPlugin); virtual ~GMPVideoEncoderChild(); void Init(GMPVideoEncoder* aEncoder); GMPVideoHostImpl& Host(); - // GMPEncoderCallback + // GMPVideoEncoderCallback virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo) MOZ_OVERRIDE; + const uint8_t* aCodecSpecificInfo, + uint32_t aCodecSpecificInfoLength) MOZ_OVERRIDE; // GMPSharedMemManager - virtual bool MgrAllocShmem(size_t aSize, - ipc::Shmem::SharedMemory::SharedMemoryType aType, - ipc::Shmem* aMem) MOZ_OVERRIDE; - virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE; + virtual void CheckThread(); + virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem) + { +#ifndef SHMEM_ALLOC_IN_CHILD + return CallNeedShmem(aSize, aMem); +#else +#ifdef GMP_SAFE_SHMEM + return AllocShmem(aSize, aType, aMem); +#else + return AllocUnsafeShmem(aSize, aType, aMem); +#endif +#endif + } + virtual void Dealloc(Shmem& aMem) + { +#ifndef SHMEM_ALLOC_IN_CHILD + SendParentShmemForPool(aMem); +#else + DeallocShmem(aMem); +#endif + } private: // PGMPVideoEncoderChild virtual bool RecvInitEncode(const GMPVideoCodec& aCodecSettings, + const nsTArray<uint8_t>& aCodecSpecific, const int32_t& aNumberOfCores, const uint32_t& aMaxPayloadSize) MOZ_OVERRIDE; virtual bool RecvEncode(const GMPVideoi420FrameData& aInputFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo, - const InfallibleTArray<int>& aFrameTypes) MOZ_OVERRIDE; + const nsTArray<uint8_t>& aCodecSpecificInfo, + const nsTArray<GMPVideoFrameType>& aFrameTypes) MOZ_OVERRIDE; + virtual bool RecvChildShmemForPool(Shmem& aEncodedBuffer) MOZ_OVERRIDE; virtual bool RecvSetChannelParameters(const uint32_t& aPacketLoss, const uint32_t& aRTT) MOZ_OVERRIDE; virtual bool RecvSetRates(const uint32_t& aNewBitRate, const uint32_t& aFrameRate) MOZ_OVERRIDE; virtual bool RecvSetPeriodicKeyFrames(const bool& aEnable) MOZ_OVERRIDE; virtual bool RecvEncodingComplete() MOZ_OVERRIDE; GMPChild* mPlugin;
--- a/content/media/gmp/GMPVideoEncoderParent.cpp +++ b/content/media/gmp/GMPVideoEncoderParent.cpp @@ -38,147 +38,132 @@ GMPVideoEncoderParent::~GMPVideoEncoderP } GMPVideoHostImpl& GMPVideoEncoderParent::Host() { return mVideoHost; } -bool -GMPVideoEncoderParent::MgrAllocShmem(size_t aSize, - ipc::Shmem::SharedMemory::SharedMemoryType aType, - ipc::Shmem* aMem) -{ - MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); - - return AllocShmem(aSize, aType, aMem); -} - -bool -GMPVideoEncoderParent::MgrDeallocShmem(Shmem& aMem) -{ - MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); - - return DeallocShmem(aMem); -} - -GMPVideoErr +GMPErr GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings, - GMPEncoderCallback* aCallback, + const nsTArray<uint8_t>& aCodecSpecific, + GMPVideoEncoderCallbackProxy* aCallback, int32_t aNumberOfCores, uint32_t aMaxPayloadSize) { if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video encoder!"); - return GMPVideoGenericErr; + return GMPGenericErr; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!aCallback) { - return GMPVideoGenericErr; + return GMPGenericErr; } mCallback = aCallback; - if (!SendInitEncode(aCodecSettings, aNumberOfCores, aMaxPayloadSize)) { - return GMPVideoGenericErr; + if (!SendInitEncode(aCodecSettings, aCodecSpecific, aNumberOfCores, aMaxPayloadSize)) { + return GMPGenericErr; } // Async IPC, we don't have access to a return value. - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo, - const std::vector<GMPVideoFrameType>& aFrameTypes) + const nsTArray<uint8_t>& aCodecSpecificInfo, + const nsTArray<GMPVideoFrameType>& aFrameTypes) { nsAutoRef<GMPVideoi420Frame> frameRef(aInputFrame); if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video encoder!"); - return GMPVideoGenericErr; + return GMPGenericErr; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); auto inputFrameImpl = static_cast<GMPVideoi420FrameImpl*>(aInputFrame); GMPVideoi420FrameData frameData; inputFrameImpl->InitFrameData(frameData); - InfallibleTArray<int> frameTypes; - frameTypes.SetCapacity(aFrameTypes.size()); - for (std::vector<int>::size_type i = 0; i != aFrameTypes.size(); i++) { - frameTypes.AppendElement(static_cast<int>(aFrameTypes[i])); + // Very rough kill-switch if the plugin stops processing. If it's merely + // hung and continues, we'll come back to life eventually. + // 3* is because we're using 3 buffers per frame for i420 data for now. + if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit || + NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) { + return GMPGenericErr; } if (!SendEncode(frameData, aCodecSpecificInfo, - frameTypes)) { - return GMPVideoGenericErr; + aFrameTypes)) { + return GMPGenericErr; } // Async IPC, we don't have access to a return value. - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) { if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video encoder!"); - return GMPVideoGenericErr; + return GMPGenericErr; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendSetChannelParameters(aPacketLoss, aRTT)) { - return GMPVideoGenericErr; + return GMPGenericErr; } // Async IPC, we don't have access to a return value. - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) { if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video encoder!"); - return GMPVideoGenericErr; + return GMPGenericErr; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendSetRates(aNewBitRate, aFrameRate)) { - return GMPVideoGenericErr; + return GMPGenericErr; } // Async IPC, we don't have access to a return value. - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable) { if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video encoder!"); - return GMPVideoGenericErr; + return GMPGenericErr; } MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); if (!SendSetPeriodicKeyFrames(aEnable)) { - return GMPVideoGenericErr; + return GMPGenericErr; } // Async IPC, we don't have access to a return value. - return GMPVideoNoErr; + return GMPNoErr; } // Note: Consider keeping ActorDestroy sync'd up when making changes here. void GMPVideoEncoderParent::EncodingComplete() { if (!mCanSendMessages) { NS_WARNING("Trying to use an invalid GMP video encoder!"); @@ -205,29 +190,64 @@ GMPVideoEncoderParent::ActorDestroy(Acto mPlugin->VideoEncoderDestroyed(this); mPlugin = nullptr; } mCanSendMessages = false; mCallback = nullptr; mVideoHost.ActorDestroyed(); } +void +GMPVideoEncoderParent::CheckThread() +{ + MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); +} + bool GMPVideoEncoderParent::RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo) + const nsTArray<uint8_t>& aCodecSpecificInfo) { if (!mCallback) { return false; } auto f = new GMPVideoEncodedFrameImpl(aEncodedFrame, &mVideoHost); // Ignore any return code. It is OK for this to fail without killing the process. mCallback->Encoded(f, aCodecSpecificInfo); + // Return SHM to sender to recycle + //SendEncodedReturn(aEncodedFrame, aCodecSpecificInfo); + return true; +} + +bool +GMPVideoEncoderParent::RecvParentShmemForPool(Shmem& aFrameBuffer) +{ + if (aFrameBuffer.IsWritable()) { + mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPFrameData, + aFrameBuffer); + } + return true; +} + +bool +GMPVideoEncoderParent::AnswerNeedShmem(const uint32_t& aEncodedBufferSize, + Shmem* aMem) +{ + ipc::Shmem mem; + + if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData, + aEncodedBufferSize, + ipc::SharedMemory::TYPE_BASIC, &mem)) + { + return false; + } + *aMem = mem; + mem = ipc::Shmem(); return true; } bool GMPVideoEncoderParent::Recv__delete__() { if (mPlugin) { // Ignore any return code. It is OK for this to fail without killing the process.
--- a/content/media/gmp/GMPVideoEncoderParent.h +++ b/content/media/gmp/GMPVideoEncoderParent.h @@ -7,63 +7,77 @@ #define GMPVideoEncoderParent_h_ #include "mozilla/RefPtr.h" #include "gmp-video-encode.h" #include "mozilla/gmp/PGMPVideoEncoderParent.h" #include "GMPMessageUtils.h" #include "GMPSharedMemManager.h" #include "GMPVideoHost.h" +#include "GMPVideoEncoderProxy.h" namespace mozilla { namespace gmp { class GMPParent; -class GMPVideoEncoderParent : public GMPVideoEncoder, +class GMPVideoEncoderParent : public GMPVideoEncoderProxy, public PGMPVideoEncoderParent, public GMPSharedMemManager { public: NS_INLINE_DECL_REFCOUNTING(GMPVideoEncoderParent) GMPVideoEncoderParent(GMPParent *aPlugin); GMPVideoHostImpl& Host(); - // GMPSharedMemManager - virtual bool MgrAllocShmem(size_t aSize, - ipc::Shmem::SharedMemory::SharedMemoryType aType, - ipc::Shmem* aMem) MOZ_OVERRIDE; - virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE; + // GMPVideoEncoderProxy + virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings, + const nsTArray<uint8_t>& aCodecSpecific, + GMPVideoEncoderCallbackProxy* aCallback, + int32_t aNumberOfCores, + uint32_t aMaxPayloadSize) MOZ_OVERRIDE; + virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame, + const nsTArray<uint8_t>& aCodecSpecificInfo, + const nsTArray<GMPVideoFrameType>& aFrameTypes) MOZ_OVERRIDE; + virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) MOZ_OVERRIDE; + virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) MOZ_OVERRIDE; + virtual GMPErr SetPeriodicKeyFrames(bool aEnable) MOZ_OVERRIDE; + virtual void EncodingComplete() MOZ_OVERRIDE; - // GMPVideoEncoder - virtual GMPVideoErr InitEncode(const GMPVideoCodec& aCodecSettings, - GMPEncoderCallback* aCallback, - int32_t aNumberOfCores, - uint32_t aMaxPayloadSize) MOZ_OVERRIDE; - virtual GMPVideoErr Encode(GMPVideoi420Frame* aInputFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo, - const std::vector<GMPVideoFrameType>& aFrameTypes) MOZ_OVERRIDE; - virtual GMPVideoErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) MOZ_OVERRIDE; - virtual GMPVideoErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) MOZ_OVERRIDE; - virtual GMPVideoErr SetPeriodicKeyFrames(bool aEnable) MOZ_OVERRIDE; - virtual void EncodingComplete() MOZ_OVERRIDE; + // GMPSharedMemManager + virtual void CheckThread(); + virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem) + { +#ifdef GMP_SAFE_SHMEM + return AllocShmem(aSize, aType, aMem); +#else + return AllocUnsafeShmem(aSize, aType, aMem); +#endif + } + virtual void Dealloc(Shmem& aMem) + { + DeallocShmem(aMem); + } private: virtual ~GMPVideoEncoderParent(); // PGMPVideoEncoderParent virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; virtual bool RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo) MOZ_OVERRIDE; + const nsTArray<uint8_t>& aCodecSpecificInfo) MOZ_OVERRIDE; + virtual bool RecvParentShmemForPool(Shmem& aFrameBuffer) MOZ_OVERRIDE; + virtual bool AnswerNeedShmem(const uint32_t& aEncodedBufferSize, + Shmem* aMem) MOZ_OVERRIDE; virtual bool Recv__delete__() MOZ_OVERRIDE; bool mCanSendMessages; GMPParent* mPlugin; - GMPEncoderCallback* mCallback; + GMPVideoEncoderCallbackProxy* mCallback; GMPVideoHostImpl mVideoHost; }; } // namespace gmp } // namespace mozilla #endif // GMPVideoEncoderParent_h_
new file mode 100644 --- /dev/null +++ b/content/media/gmp/GMPVideoEncoderProxy.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GMPVideoEncoderProxy_h_ +#define GMPVideoEncoderProxy_h_ + +#include "nsTArray.h" +#include "gmp-video-encode.h" +#include "gmp-video-frame-i420.h" +#include "gmp-video-frame-encoded.h" + +class GMPVideoEncoderCallbackProxy { +public: + virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame, + const nsTArray<uint8_t>& aCodecSpecificInfo) = 0; +}; + +// A proxy to GMPVideoEncoder in the child process. +// GMPVideoEncoderParent exposes this to users the GMP. +// This enables Gecko to pass nsTArrays to the child GMP and avoid +// an extra copy when doing so. +class GMPVideoEncoderProxy { +public: + virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings, + const nsTArray<uint8_t>& aCodecSpecific, + GMPVideoEncoderCallbackProxy* aCallback, + int32_t aNumberOfCores, + uint32_t aMaxPayloadSize) = 0; + virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame, + const nsTArray<uint8_t>& aCodecSpecificInfo, + const nsTArray<GMPVideoFrameType>& aFrameTypes) = 0; + virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0; + virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0; + virtual GMPErr SetPeriodicKeyFrames(bool aEnable) = 0; + virtual void EncodingComplete() = 0; +}; + +#endif // GMPVideoEncoderProxy_h_
--- a/content/media/gmp/GMPVideoHost.cpp +++ b/content/media/gmp/GMPVideoHost.cpp @@ -15,59 +15,59 @@ GMPVideoHostImpl::GMPVideoHostImpl(GMPSh : mSharedMemMgr(aSharedMemMgr) { } GMPVideoHostImpl::~GMPVideoHostImpl() { } -GMPVideoErr +GMPErr GMPVideoHostImpl::CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) { if (!mSharedMemMgr) { - return GMPVideoGenericErr; + return GMPGenericErr; } if (!aFrame) { - return GMPVideoGenericErr; + return GMPGenericErr; } *aFrame = nullptr; switch (aFormat) { case kGMPI420VideoFrame: *aFrame = new GMPVideoi420FrameImpl(this); - return GMPVideoNoErr; + return GMPNoErr; case kGMPEncodedVideoFrame: *aFrame = new GMPVideoEncodedFrameImpl(this); - return GMPVideoNoErr; + return GMPNoErr; default: NS_NOTREACHED("Unknown frame format!"); } - return GMPVideoGenericErr; + return GMPGenericErr; } -GMPVideoErr +GMPErr GMPVideoHostImpl::CreatePlane(GMPPlane** aPlane) { if (!mSharedMemMgr) { - return GMPVideoGenericErr; + return GMPGenericErr; } if (!aPlane) { - return GMPVideoGenericErr; + return GMPGenericErr; } *aPlane = nullptr; auto p = new GMPPlaneImpl(this); *aPlane = p; - return GMPVideoNoErr; + return GMPNoErr; } GMPSharedMemManager* GMPVideoHostImpl::SharedMemMgr() { return mSharedMemMgr; }
--- a/content/media/gmp/GMPVideoHost.h +++ b/content/media/gmp/GMPVideoHost.h @@ -30,18 +30,18 @@ public: void DoneWithAPI(); void ActorDestroyed(); void PlaneCreated(GMPPlaneImpl* aPlane); void PlaneDestroyed(GMPPlaneImpl* aPlane); void EncodedFrameCreated(GMPVideoEncodedFrameImpl* aEncodedFrame); void EncodedFrameDestroyed(GMPVideoEncodedFrameImpl* aFrame); // GMPVideoHost - virtual GMPVideoErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) MOZ_OVERRIDE; - virtual GMPVideoErr CreatePlane(GMPPlane** aPlane) MOZ_OVERRIDE; + virtual GMPErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) MOZ_OVERRIDE; + virtual GMPErr CreatePlane(GMPPlane** aPlane) MOZ_OVERRIDE; private: // All shared memory allocations have to be made by an IPDL actor. // This is a reference to the owning actor. If this reference is // null then the actor has died and all allocations must fail. GMPSharedMemManager* mSharedMemMgr; // We track all of these things because they need to handle further
--- a/content/media/gmp/GMPVideoPlaneImpl.cpp +++ b/content/media/gmp/GMPVideoPlaneImpl.cpp @@ -68,106 +68,107 @@ GMPPlaneImpl::InitPlaneData(GMPPlaneData // This method is called right before Shmem is sent to another process. // We need to effectively zero out our member copy so that we don't // try to delete memory we don't own later. mBuffer = ipc::Shmem(); return true; } -GMPVideoErr +GMPErr GMPPlaneImpl::MaybeResize(int32_t aNewSize) { if (aNewSize <= AllocatedSize()) { - return GMPVideoNoErr; + return GMPNoErr; } if (!mHost) { - return GMPVideoGenericErr; + return GMPGenericErr; } ipc::Shmem new_mem; - if (!mHost->SharedMemMgr()->MgrAllocShmem(aNewSize, ipc::SharedMemory::TYPE_BASIC, &new_mem) || + if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPFrameData, aNewSize, + ipc::SharedMemory::TYPE_BASIC, &new_mem) || !new_mem.get<uint8_t>()) { - return GMPVideoAllocErr; + return GMPAllocErr; } if (mBuffer.IsReadable()) { memcpy(new_mem.get<uint8_t>(), Buffer(), mSize); } DestroyBuffer(); mBuffer = new_mem; - return GMPVideoNoErr; + return GMPNoErr; } void GMPPlaneImpl::DestroyBuffer() { if (mHost && mBuffer.IsWritable()) { - mHost->SharedMemMgr()->MgrDeallocShmem(mBuffer); + mHost->SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPFrameData, mBuffer); } mBuffer = ipc::Shmem(); } -GMPVideoErr +GMPErr GMPPlaneImpl::CreateEmptyPlane(int32_t aAllocatedSize, int32_t aStride, int32_t aPlaneSize) { if (aAllocatedSize < 1 || aStride < 1 || aPlaneSize < 1) { - return GMPVideoGenericErr; + return GMPGenericErr; } - GMPVideoErr err = MaybeResize(aAllocatedSize); - if (err != GMPVideoNoErr) { + GMPErr err = MaybeResize(aAllocatedSize); + if (err != GMPNoErr) { return err; } mSize = aPlaneSize; mStride = aStride; - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPPlaneImpl::Copy(const GMPPlane& aPlane) { auto& planeimpl = static_cast<const GMPPlaneImpl&>(aPlane); - GMPVideoErr err = MaybeResize(planeimpl.mSize); - if (err != GMPVideoNoErr) { + GMPErr err = MaybeResize(planeimpl.mSize); + if (err != GMPNoErr) { return err; } if (planeimpl.Buffer() && planeimpl.mSize > 0) { memcpy(Buffer(), planeimpl.Buffer(), mSize); } mSize = planeimpl.mSize; mStride = planeimpl.mStride; - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPPlaneImpl::Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer) { - GMPVideoErr err = MaybeResize(aSize); - if (err != GMPVideoNoErr) { + GMPErr err = MaybeResize(aSize); + if (err != GMPNoErr) { return err; } if (aBuffer && aSize > 0) { memcpy(Buffer(), aBuffer, aSize); } mSize = aSize; mStride = aStride; - return GMPVideoNoErr; + return GMPNoErr; } void GMPPlaneImpl::Swap(GMPPlane& aPlane) { auto& planeimpl = static_cast<GMPPlaneImpl&>(aPlane); std::swap(mStride, planeimpl.mStride);
--- a/content/media/gmp/GMPVideoPlaneImpl.h +++ b/content/media/gmp/GMPVideoPlaneImpl.h @@ -29,34 +29,34 @@ public: // This is called when something has gone wrong - specicifically, // a child process has crashed. Does not attempt to release Shmem, // as the Shmem has already been released. void ActorDestroyed(); bool InitPlaneData(GMPPlaneData& aPlaneData); // GMPPlane - virtual GMPVideoErr CreateEmptyPlane(int32_t aAllocatedSize, - int32_t aStride, - int32_t aPlaneSize) MOZ_OVERRIDE; - virtual GMPVideoErr Copy(const GMPPlane& aPlane) MOZ_OVERRIDE; - virtual GMPVideoErr Copy(int32_t aSize, - int32_t aStride, - const uint8_t* aBuffer) MOZ_OVERRIDE; + virtual GMPErr CreateEmptyPlane(int32_t aAllocatedSize, + int32_t aStride, + int32_t aPlaneSize) MOZ_OVERRIDE; + virtual GMPErr Copy(const GMPPlane& aPlane) MOZ_OVERRIDE; + virtual GMPErr Copy(int32_t aSize, + int32_t aStride, + const uint8_t* aBuffer) MOZ_OVERRIDE; virtual void Swap(GMPPlane& aPlane) MOZ_OVERRIDE; virtual int32_t AllocatedSize() const MOZ_OVERRIDE; virtual void ResetSize() MOZ_OVERRIDE; virtual bool IsZeroSize() const MOZ_OVERRIDE; virtual int32_t Stride() const MOZ_OVERRIDE; virtual const uint8_t* Buffer() const MOZ_OVERRIDE; virtual uint8_t* Buffer() MOZ_OVERRIDE; virtual void Destroy() MOZ_OVERRIDE; private: - GMPVideoErr MaybeResize(int32_t aNewSize); + GMPErr MaybeResize(int32_t aNewSize); void DestroyBuffer(); ipc::Shmem mBuffer; int32_t mSize; int32_t mStride; GMPVideoHostImpl* mHost; };
--- a/content/media/gmp/GMPVideoi420FrameImpl.cpp +++ b/content/media/gmp/GMPVideoi420FrameImpl.cpp @@ -10,31 +10,31 @@ namespace mozilla { namespace gmp { GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost) : mYPlane(aHost), mUPlane(aHost), mVPlane(aHost), mWidth(0), mHeight(0), - mTimestamp(0), - mRenderTime_ms(0) + mTimestamp(0ll), + mDuration(0ll) { MOZ_ASSERT(aHost); } GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrameData, GMPVideoHostImpl* aHost) : mYPlane(aFrameData.mYPlane(), aHost), mUPlane(aFrameData.mUPlane(), aHost), mVPlane(aFrameData.mVPlane(), aHost), mWidth(aFrameData.mWidth()), mHeight(aFrameData.mHeight()), mTimestamp(aFrameData.mTimestamp()), - mRenderTime_ms(aFrameData.mRenderTime_ms()) + mDuration(aFrameData.mDuration()) { MOZ_ASSERT(aHost); } GMPVideoi420FrameImpl::~GMPVideoi420FrameImpl() { } @@ -42,17 +42,17 @@ bool GMPVideoi420FrameImpl::InitFrameData(GMPVideoi420FrameData& aFrameData) { mYPlane.InitPlaneData(aFrameData.mYPlane()); mUPlane.InitPlaneData(aFrameData.mUPlane()); mVPlane.InitPlaneData(aFrameData.mVPlane()); aFrameData.mWidth() = mWidth; aFrameData.mHeight() = mHeight; aFrameData.mTimestamp() = mTimestamp; - aFrameData.mRenderTime_ms() = mRenderTime_ms; + aFrameData.mDuration() = mDuration; return true; } GMPVideoFrameFormat GMPVideoi420FrameImpl::GetFrameFormat() { return kGMPI420VideoFrame; } @@ -101,127 +101,127 @@ GMPVideoi420FrameImpl::GetPlane(GMPPlane case kGMPVPlane : return &mVPlane; default: MOZ_CRASH("Unknown plane type!"); } return nullptr; } -GMPVideoErr +GMPErr GMPVideoi420FrameImpl::CreateEmptyFrame(int32_t aWidth, int32_t aHeight, int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) { if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v)) { - return GMPVideoGenericErr; + return GMPGenericErr; } int32_t size_y = aStride_y * aHeight; int32_t half_height = (aHeight + 1) / 2; int32_t size_u = aStride_u * half_height; int32_t size_v = aStride_v * half_height; - GMPVideoErr err = mYPlane.CreateEmptyPlane(size_y, aStride_y, size_y); - if (err != GMPVideoNoErr) { + GMPErr err = mYPlane.CreateEmptyPlane(size_y, aStride_y, size_y); + if (err != GMPNoErr) { return err; } err = mUPlane.CreateEmptyPlane(size_u, aStride_u, size_u); - if (err != GMPVideoNoErr) { + if (err != GMPNoErr) { return err; } err = mVPlane.CreateEmptyPlane(size_v, aStride_v, size_v); - if (err != GMPVideoNoErr) { + if (err != GMPNoErr) { return err; } mWidth = aWidth; mHeight = aHeight; - mTimestamp = 0; - mRenderTime_ms = 0; + mTimestamp = 0ll; + mDuration = 0ll; - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPVideoi420FrameImpl::CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y, int32_t aSize_u, const uint8_t* aBuffer_u, int32_t aSize_v, const uint8_t* aBuffer_v, int32_t aWidth, int32_t aHeight, int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) { MOZ_ASSERT(aBuffer_y); MOZ_ASSERT(aBuffer_u); MOZ_ASSERT(aBuffer_v); if (aSize_y < 1 || aSize_u < 1 || aSize_v < 1) { - return GMPVideoGenericErr; + return GMPGenericErr; } if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v)) { - return GMPVideoGenericErr; + return GMPGenericErr; } - GMPVideoErr err = mYPlane.Copy(aSize_y, aStride_y, aBuffer_y); - if (err != GMPVideoNoErr) { + GMPErr err = mYPlane.Copy(aSize_y, aStride_y, aBuffer_y); + if (err != GMPNoErr) { return err; } err = mUPlane.Copy(aSize_u, aStride_u, aBuffer_u); - if (err != GMPVideoNoErr) { + if (err != GMPNoErr) { return err; } err = mVPlane.Copy(aSize_v, aStride_v, aBuffer_v); - if (err != GMPVideoNoErr) { + if (err != GMPNoErr) { return err; } mWidth = aWidth; mHeight = aHeight; - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPVideoi420FrameImpl::CopyFrame(const GMPVideoi420Frame& aFrame) { auto& f = static_cast<const GMPVideoi420FrameImpl&>(aFrame); - GMPVideoErr err = mYPlane.Copy(f.mYPlane); - if (err != GMPVideoNoErr) { + GMPErr err = mYPlane.Copy(f.mYPlane); + if (err != GMPNoErr) { return err; } err = mUPlane.Copy(f.mUPlane); - if (err != GMPVideoNoErr) { + if (err != GMPNoErr) { return err; } err = mVPlane.Copy(f.mVPlane); - if (err != GMPVideoNoErr) { + if (err != GMPNoErr) { return err; } mWidth = f.mWidth; mHeight = f.mHeight; mTimestamp = f.mTimestamp; - mRenderTime_ms = f.mRenderTime_ms; + mDuration = f.mDuration; - return GMPVideoNoErr; + return GMPNoErr; } void GMPVideoi420FrameImpl::SwapFrame(GMPVideoi420Frame* aFrame) { auto f = static_cast<GMPVideoi420FrameImpl*>(aFrame); mYPlane.Swap(f->mYPlane); mUPlane.Swap(f->mUPlane); mVPlane.Swap(f->mVPlane); std::swap(mWidth, f->mWidth); std::swap(mHeight, f->mHeight); std::swap(mTimestamp, f->mTimestamp); - std::swap(mRenderTime_ms, f->mRenderTime_ms); + std::swap(mDuration, f->mDuration); } uint8_t* GMPVideoi420FrameImpl::Buffer(GMPPlaneType aType) { GMPPlane* p = GetPlane(aType); if (p) { return p->Buffer(); @@ -254,74 +254,74 @@ GMPVideoi420FrameImpl::Stride(GMPPlaneTy { const GMPPlane* p = GetPlane(aType); if (p) { return p->Stride(); } return -1; } -GMPVideoErr +GMPErr GMPVideoi420FrameImpl::SetWidth(int32_t aWidth) { if (!CheckDimensions(aWidth, mHeight, mYPlane.Stride(), mUPlane.Stride(), mVPlane.Stride())) { - return GMPVideoGenericErr; + return GMPGenericErr; } mWidth = aWidth; - return GMPVideoNoErr; + return GMPNoErr; } -GMPVideoErr +GMPErr GMPVideoi420FrameImpl::SetHeight(int32_t aHeight) { if (!CheckDimensions(mWidth, aHeight, mYPlane.Stride(), mUPlane.Stride(), mVPlane.Stride())) { - return GMPVideoGenericErr; + return GMPGenericErr; } mHeight = aHeight; - return GMPVideoNoErr; + return GMPNoErr; } int32_t GMPVideoi420FrameImpl::Width() const { return mWidth; } int32_t GMPVideoi420FrameImpl::Height() const { return mHeight; } void -GMPVideoi420FrameImpl::SetTimestamp(uint32_t aTimestamp) +GMPVideoi420FrameImpl::SetTimestamp(uint64_t aTimestamp) { mTimestamp = aTimestamp; } -uint32_t +uint64_t GMPVideoi420FrameImpl::Timestamp() const { return mTimestamp; } void -GMPVideoi420FrameImpl::SetRenderTime_ms(int64_t aRenderTime_ms) +GMPVideoi420FrameImpl::SetDuration(uint64_t aDuration) { - mRenderTime_ms = aRenderTime_ms; + mDuration = aDuration; } -int64_t -GMPVideoi420FrameImpl::RenderTime_ms() const +uint64_t +GMPVideoi420FrameImpl::Duration() const { - return mRenderTime_ms; + return mDuration; } bool GMPVideoi420FrameImpl::IsZeroSize() const { return (mYPlane.IsZeroSize() && mUPlane.IsZeroSize() && mVPlane.IsZeroSize()); }
--- a/content/media/gmp/GMPVideoi420FrameImpl.h +++ b/content/media/gmp/GMPVideoi420FrameImpl.h @@ -27,55 +27,55 @@ public: const GMPPlaneImpl* GetPlane(GMPPlaneType aType) const; GMPPlaneImpl* GetPlane(GMPPlaneType aType); // GMPVideoFrame virtual GMPVideoFrameFormat GetFrameFormat() MOZ_OVERRIDE; virtual void Destroy() MOZ_OVERRIDE; // GMPVideoi420Frame - virtual GMPVideoErr CreateEmptyFrame(int32_t aWidth, - int32_t aHeight, - int32_t aStride_y, - int32_t aStride_u, - int32_t aStride_v) MOZ_OVERRIDE; - virtual GMPVideoErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y, - int32_t aSize_u, const uint8_t* aBuffer_u, - int32_t aSize_v, const uint8_t* aBuffer_v, - int32_t aWidth, + virtual GMPErr CreateEmptyFrame(int32_t aWidth, int32_t aHeight, int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) MOZ_OVERRIDE; - virtual GMPVideoErr CopyFrame(const GMPVideoi420Frame& aFrame) MOZ_OVERRIDE; + virtual GMPErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y, + int32_t aSize_u, const uint8_t* aBuffer_u, + int32_t aSize_v, const uint8_t* aBuffer_v, + int32_t aWidth, + int32_t aHeight, + int32_t aStride_y, + int32_t aStride_u, + int32_t aStride_v) MOZ_OVERRIDE; + virtual GMPErr CopyFrame(const GMPVideoi420Frame& aFrame) MOZ_OVERRIDE; virtual void SwapFrame(GMPVideoi420Frame* aFrame) MOZ_OVERRIDE; virtual uint8_t* Buffer(GMPPlaneType aType) MOZ_OVERRIDE; virtual const uint8_t* Buffer(GMPPlaneType aType) const MOZ_OVERRIDE; virtual int32_t AllocatedSize(GMPPlaneType aType) const MOZ_OVERRIDE; virtual int32_t Stride(GMPPlaneType aType) const MOZ_OVERRIDE; - virtual GMPVideoErr SetWidth(int32_t aWidth) MOZ_OVERRIDE; - virtual GMPVideoErr SetHeight(int32_t aHeight) MOZ_OVERRIDE; + virtual GMPErr SetWidth(int32_t aWidth) MOZ_OVERRIDE; + virtual GMPErr SetHeight(int32_t aHeight) MOZ_OVERRIDE; virtual int32_t Width() const MOZ_OVERRIDE; virtual int32_t Height() const MOZ_OVERRIDE; - virtual void SetTimestamp(uint32_t aTimestamp) MOZ_OVERRIDE; - virtual uint32_t Timestamp() const MOZ_OVERRIDE; - virtual void SetRenderTime_ms(int64_t aRenderTime_ms) MOZ_OVERRIDE; - virtual int64_t RenderTime_ms() const MOZ_OVERRIDE; + virtual void SetTimestamp(uint64_t aTimestamp) MOZ_OVERRIDE; + virtual uint64_t Timestamp() const MOZ_OVERRIDE; + virtual void SetDuration(uint64_t aDuration) MOZ_OVERRIDE; + virtual uint64_t Duration() const MOZ_OVERRIDE; virtual bool IsZeroSize() const MOZ_OVERRIDE; virtual void ResetSize() MOZ_OVERRIDE; private: bool CheckDimensions(int32_t aWidth, int32_t aHeight, int32_t aStride_y, int32_t aStride_u, int32_t aStride_v); GMPPlaneImpl mYPlane; GMPPlaneImpl mUPlane; GMPPlaneImpl mVPlane; int32_t mWidth; int32_t mHeight; - uint32_t mTimestamp; - int64_t mRenderTime_ms; + uint64_t mTimestamp; + uint64_t mDuration; }; } // namespace gmp } // namespace mozilla #endif // GMPVideoi420FrameImpl_h_
--- a/content/media/gmp/PGMP.ipdl +++ b/content/media/gmp/PGMP.ipdl @@ -4,19 +4,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PGMPVideoDecoder; include protocol PGMPVideoEncoder; namespace mozilla { namespace gmp { -async protocol PGMP +intr protocol PGMP { manages PGMPVideoDecoder; manages PGMPVideoEncoder; child: - PGMPVideoDecoder(); - PGMPVideoEncoder(); + async PGMPVideoDecoder(); + async PGMPVideoEncoder(); }; } // namespace gmp } // namespace mozilla
--- a/content/media/gmp/PGMPVideoDecoder.ipdl +++ b/content/media/gmp/PGMPVideoDecoder.ipdl @@ -2,38 +2,46 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PGMP; include GMPTypes; using GMPVideoCodec from "gmp-video-codec.h"; -using GMPCodecSpecificInfo from "gmp-video-codec.h"; include "GMPMessageUtils.h"; namespace mozilla { namespace gmp { -async protocol PGMPVideoDecoder +intr protocol PGMPVideoDecoder { manager PGMP; child: - InitDecode(GMPVideoCodec aCodecSettings, - int32_t aCoreCount); - Decode(GMPVideoEncodedFrameData aInputFrame, - bool aMissingFrames, - GMPCodecSpecificInfo aCodecSpecificInfo, - int64_t aRenderTimeMs); - Reset(); - Drain(); - DecodingComplete(); + async InitDecode(GMPVideoCodec aCodecSettings, + uint8_t[] aCodecSpecific, + int32_t aCoreCount); + async Decode(GMPVideoEncodedFrameData aInputFrame, + bool aMissingFrames, + uint8_t[] aCodecSpecificInfo, + int64_t aRenderTimeMs); + async Reset(); + async Drain(); + async DecodingComplete(); + async ChildShmemForPool(Shmem aFrameBuffer); + parent: - __delete__(); - Decoded(GMPVideoi420FrameData aDecodedFrame); - ReceivedDecodedReferenceFrame(uint64_t aPictureId); - ReceivedDecodedFrame(uint64_t aPictureId); - InputDataExhausted(); + async __delete__(); + async Decoded(GMPVideoi420FrameData aDecodedFrame); + async ReceivedDecodedReferenceFrame(uint64_t aPictureId); + async ReceivedDecodedFrame(uint64_t aPictureId); + async InputDataExhausted(); + async DrainComplete(); + async ResetComplete(); + async ParentShmemForPool(Shmem aEncodedBuffer); + // MUST be intr - if sync and we create a new Shmem, when the returned + // Shmem is received in the Child it will fail to Deserialize + intr NeedShmem(uint32_t aFrameBufferSize) returns (Shmem aMem); }; } // namespace gmp } // namespace mozilla
--- a/content/media/gmp/PGMPVideoEncoder.ipdl +++ b/content/media/gmp/PGMPVideoEncoder.ipdl @@ -2,38 +2,44 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PGMP; include GMPTypes; using GMPVideoCodec from "gmp-video-codec.h"; -using GMPCodecSpecificInfo from "gmp-video-codec.h"; +using GMPVideoFrameType from "gmp-video-frame-encoded.h"; include "GMPMessageUtils.h"; namespace mozilla { namespace gmp { -async protocol PGMPVideoEncoder +intr protocol PGMPVideoEncoder { manager PGMP; child: - InitEncode(GMPVideoCodec aCodecSettings, - int32_t aNumberOfCores, - uint32_t aMaxPayloadSize); - Encode(GMPVideoi420FrameData aInputFrame, - GMPCodecSpecificInfo aCodecSpecificInfo, - int[] aFrameTypes); - SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT); - SetRates(uint32_t aNewBitRate, uint32_t aFrameRate); - SetPeriodicKeyFrames(bool aEnable); - EncodingComplete(); + async InitEncode(GMPVideoCodec aCodecSettings, + uint8_t[] aCodecSpecific, + int32_t aNumberOfCores, + uint32_t aMaxPayloadSize); + async Encode(GMPVideoi420FrameData aInputFrame, + uint8_t[] aCodecSpecificInfo, + GMPVideoFrameType[] aFrameTypes); + async SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT); + async SetRates(uint32_t aNewBitRate, uint32_t aFrameRate); + async SetPeriodicKeyFrames(bool aEnable); + async EncodingComplete(); + async ChildShmemForPool(Shmem aEncodedBuffer); parent: - __delete__(); - Encoded(GMPVideoEncodedFrameData aEncodedFrame, - GMPCodecSpecificInfo aCodecSpecificInfo); + async __delete__(); + async Encoded(GMPVideoEncodedFrameData aEncodedFrame, + uint8_t[] aCodecSpecificInfo); + async ParentShmemForPool(Shmem aFrameBuffer); + // MUST be intr - if sync and we create a new Shmem, when the returned + // Shmem is received in the Child it will fail to Deserialize + intr NeedShmem(uint32_t aEncodedBufferSize) returns (Shmem aMem); }; } // namespace gmp } // namespace mozilla
new file mode 100644 --- /dev/null +++ b/content/media/gmp/gmp-api/gmp-async-shutdown.h @@ -0,0 +1,52 @@ +/* +* Copyright 2013, Mozilla Foundation and contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef GMP_ASYNC_SHUTDOWN_H_ +#define GMP_ASYNC_SHUTDOWN_H_ + +// API exposed by the plugin library to manage asynchronous shutdown. +// Some plugins require special cleanup which may need to make calls +// to host services and wait for async responses. +// +// To enable a plugins to block shutdown until its async shutdown is +// complete, implement the GMPAsyncShutdown interface and return it when +// your plugin's GMPGetAPI function is called with "async-shutdown". +// When your GMPAsyncShutdown's BeginShutdown() implementation is called +// by the GMP host, you should initate your async shutdown process. +// Once you have completed shutdown, call the ShutdownComplete() function +// of the GMPAsyncShutdownHost that is passed as the host argument to the +// GMPGetAPI() call. +// +// Note: Your GMP's GMPShutdown function will still be called after your +// call to ShutdownComplete(). +// +// API name: "async-shutdown" +// Host API: GMPAsyncShutdownHost +class GMPAsyncShutdown { +public: + virtual ~GMPAsyncShutdown() {} + + virtual void BeginShutdown() = 0; +}; + +class GMPAsyncShutdownHost { +public: + virtual ~GMPAsyncShutdownHost() {} + + virtual void ShutdownComplete() = 0; +}; + +#endif // GMP_ASYNC_SHUTDOWN_H_
new file mode 100644 --- /dev/null +++ b/content/media/gmp/gmp-api/gmp-audio-codec.h @@ -0,0 +1,43 @@ +/* +* Copyright 2013, Mozilla Foundation and contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef GMP_AUDIO_CODEC_h_ +#define GMP_AUDIO_CODEC_h_ + +#include <stdint.h> + +enum GMPAudioCodecType +{ + kGMPAudioCodecAAC, + kGMPAudioCodecVorbis, + kGMPAudioCodecInvalid // Should always be last. +}; + +struct GMPAudioCodec +{ + GMPAudioCodecType mCodecType; + uint32_t mChannelCount; + uint32_t mBitsPerChannel; + uint32_t mSamplesPerSecond; + + // Codec extra data, such as vorbis setup header, or + // AAC AudioSpecificConfig. + // These are null/0 if not externally negotiated + const uint8_t* mExtraData; + size_t mExtraDataLen; +}; + +#endif // GMP_AUDIO_CODEC_h_
new file mode 100644 --- /dev/null +++ b/content/media/gmp/gmp-api/gmp-audio-decode.h @@ -0,0 +1,72 @@ +/* +* Copyright 2013, Mozilla Foundation and contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef GMP_AUDIO_DECODE_h_ +#define GMP_AUDIO_DECODE_h_ + +#include "gmp-errors.h" +#include "gmp-audio-samples.h" +#include "gmp-audio-codec.h" +#include <stdint.h> + +// ALL METHODS MUST BE CALLED ON THE MAIN THREAD +class GMPAudioDecoderCallback +{ +public: + virtual ~GMPAudioDecoderCallback() {} + + virtual void Decoded(GMPAudioSamples* aDecodedSamples) = 0; + + virtual void InputDataExhausted() = 0; + + virtual void DrainComplete() = 0; + + virtual void ResetComplete() = 0; +}; + +// ALL METHODS MUST BE CALLED ON THE MAIN THREAD +class GMPAudioDecoder +{ +public: + virtual ~GMPAudioDecoder() {} + + // aCallback: Subclass should retain reference to it until DecodingComplete + // is called. Do not attempt to delete it, host retains ownership. + // TODO: Pass AudioHost so decoder can create GMPAudioEncodedFrame objects? + virtual GMPErr InitDecode(const GMPAudioCodec& aCodecSettings, + GMPAudioDecoderCallback* aCallback) = 0; + + // Decode encoded audio frames (as a part of an audio stream). The decoded + // frames must be returned to the user through the decode complete callback. + virtual GMPErr Decode(GMPAudioSamples* aEncodedSamples) = 0; + + // Reset decoder state and prepare for a new call to Decode(...). + // Flushes the decoder pipeline. + // The decoder should enqueue a task to run ResetComplete() on the main + // thread once the reset has finished. + virtual GMPErr Reset() = 0; + + // Output decoded frames for any data in the pipeline, regardless of ordering. + // All remaining decoded frames should be immediately returned via callback. + // The decoder should enqueue a task to run DrainComplete() on the main + // thread once the reset has finished. + virtual GMPErr Drain() = 0; + + // May free decoder memory. + virtual void DecodingComplete() = 0; +}; + +#endif // GMP_VIDEO_DECODE_h_
new file mode 100644 --- /dev/null +++ b/content/media/gmp/gmp-api/gmp-audio-host.h @@ -0,0 +1,32 @@ +/* +* Copyright 2013, Mozilla Foundation and contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef GMP_AUDIO_HOST_h_ +#define GMP_AUDIO_HOST_h_ + +#include "gmp-errors.h" +#include "gmp-audio-samples.h" + +class GMPAudioHost +{ +public: + // Construct various Audio API objects. Host does not retain reference, + // caller is owner and responsible for deleting. + virtual GMPErr CreateSamples(GMPAudioFormat aFormat, + GMPAudioSamples** aSamples) = 0; +}; + +#endif // GMP_AUDIO_HOST_h_
new file mode 100644 --- /dev/null +++ b/content/media/gmp/gmp-api/gmp-audio-samples.h @@ -0,0 +1,57 @@ +/* +* Copyright 2013, Mozilla Foundation and contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef GMP_AUDIO_FRAME_h_ +#define GMP_AUDIO_FRAME_h_ + +#include <stdint.h> +#include "gmp-errors.h" +#include "gmp-decryption.h" + +enum GMPAudioFormat +{ + kGMPAudioEncodedSamples, // Raw compressed data, i.e. an AAC/Vorbis packet. + kGMPAudioIS16Samples, // Interleaved int16_t PCM samples. + kGMPAudioSamplesFormatInvalid // Should always be last. +}; + +class GMPAudioSamples { +public: + // The format of the buffer. + virtual GMPAudioFormat GetFormat() = 0; + virtual void Destroy() = 0; + + // MAIN THREAD ONLY + // Buffer size must be exactly what's required to contain all samples in + // the buffer; every byte is assumed to be part of a sample. + virtual GMPErr SetBufferSize(uint32_t aSize) = 0; + + // Size of the buffer in bytes. + virtual uint32_t Size() = 0; + + // Timestamps are in microseconds, and are the playback start time of the + // first sample in the buffer. + virtual void SetTimeStamp(uint64_t aTimeStamp) = 0; + virtual uint64_t TimeStamp() = 0; + virtual const uint8_t* Buffer() const = 0; + virtual uint8_t* Buffer() = 0; + + // Get data describing how this frame is encrypted, or nullptr if the + // buffer is not encrypted. + virtual const GMPEncryptedBufferData* GetDecryptionData() const = 0; +}; + +#endif // GMP_AUDIO_FRAME_h_
new file mode 100644 --- /dev/null +++ b/content/media/gmp/gmp-api/gmp-decryption.h @@ -0,0 +1,208 @@ +/* +* Copyright 2013, Mozilla Foundation and contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef GMP_DECRYPTION_h_ +#define GMP_DECRYPTION_h_ + +#include "gmp-platform.h" + +class GMPEncryptedBufferData { +public: + // Key ID to identify the decryption key. + virtual const uint8_t* KeyId() const = 0; + + // Size (in bytes) of |KeyId()|. + virtual uint32_t KeyIdSize() const = 0; + + // Initialization vector. + virtual const uint8_t* IV() const = 0; + + // Size (in bytes) of |IV|. + virtual uint32_t IVSize() const = 0; + + // Number of enties returned by ClearBytes and CipherBytes(). + virtual uint32_t NumSubsamples() const = 0; + + virtual const uint32_t* ClearBytes() const = 0; + + virtual const uint32_t* CipherBytes() const = 0; +}; + +// These match to the DOMException codes as per: +// http://www.w3.org/TR/dom/#domexception +enum GMPDOMException { + kGMPNoModificationAllowedError = 7, + kGMPNotFoundError = 8, + kGMPNotSupportedError = 9, + kGMPInvalidStateError = 11, + kGMPSyntaxError = 12, + kGMPInvalidModificationError = 13, + kGMPInvalidAccessError = 15, + kGMPSecurityError = 18, + kGMPAbortError = 20, + kGMPQuotaExceededError = 22, + kGMPTimeoutError = 23 +}; + +// Time in milliseconds, as offset from epoch, 1 Jan 1970. +typedef int64_t GMPTimestamp; + +class GMPDecryptorCallback { +public: + // Resolves a promise for a session created or loaded. + // Passes the session id to be exposed to JavaScript. + // Must be called before OnSessionMessage(). + // aSessionId must be null terminated. + virtual void OnResolveNewSessionPromise(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) = 0; + + // Called to resolve a specified promise with "undefined". + virtual void OnResolvePromise(uint32_t aPromiseId) = 0; + + // Called to reject a promise with a DOMException. + // aMessage is logged to the WebConsole. + // aMessage is optional, but if present must be null terminated. + virtual void OnRejectPromise(uint32_t aPromiseId, + GMPDOMException aException, + const char* aMessage, + uint32_t aMessageLength) = 0; + + // Called by the CDM when it has a message for session |session_id|. + // Length parameters should not include null termination. + // aSessionId must be null terminated. + virtual void OnSessionMessage(const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aMessage, + uint32_t aMessageLength, + const char* aDestinationURL, + uint32_t aDestinationURLLength) = 0; + + // aSessionId must be null terminated. + virtual void OnExpirationChange(const char* aSessionId, + uint32_t aSessionIdLength, + GMPTimestamp aExpiryTime) = 0; + + // Called by the GMP when a session is closed. All file IO + // that a session requires should be complete before calling this. + // aSessionId must be null terminated. + virtual void OnSessionClosed(const char* aSessionId, + uint32_t aSessionIdLength) = 0; + + // Called by the GMP when an error occurs in a session. + // aSessionId must be null terminated. + // aMessage is logged to the WebConsole. + // aMessage is optional, but if present must be null terminated. + virtual void OnSessionError(const char* aSessionId, + uint32_t aSessionIdLength, + GMPDOMException aException, + uint32_t aSystemCode, + const char* aMessage, + uint32_t aMessageLength) = 0; + + virtual void OnKeyIdUsable(const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aKeyId, + uint32_t aKeyIdLength) = 0; + + // Marks a key as no longer usable. + // Note: Keys are assumed to be not usable when a session is closed or removed. + virtual void OnKeyIdNotUsable(const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aKeyId, + uint32_t aKeyIdLength) = 0; + +}; + +// Host interface, passed to GetAPIFunc(), with "decrypt". +class GMPDecryptorHost { +public: + + // Returns an origin specific string uniquely identifying the device. + // The node id contains a random component, and is consistent between + // plugin instantiations, unless the user clears it. + // Different origins have different node ids. + // The node id pointer returned here remains valid for the until shutdown + // begins. + // *aOutNodeId is null terminated. + virtual void GetNodeId(const char** aOutNodeId, + uint32_t* aOutNodeIdLength) = 0; + + virtual void GetSandboxVoucher(const uint8_t** aVoucher, + uint8_t* aVoucherLength) = 0; + + virtual void GetPluginVoucher(const uint8_t** aVoucher, + uint8_t* aVoucherLength) = 0; +}; + +enum GMPSessionType { + kGMPTemporySession = 0, + kGMPPersistentSession = 1 +}; + +// API exposed by plugin library to manage decryption sessions. +// When the Host requests this by calling GMPGetAPIFunc(). +// +// API name: "eme-decrypt". +// Host API: GMPDecryptorHost +class GMPDecryptor { +public: + + // Sets the callback to use with the decryptor to return results + // to Gecko. + virtual void Init(GMPDecryptorCallback* aCallback) = 0; + + // Requests the creation of a session given |aType| and |aInitData|. + // Decryptor should callback GMPDecryptorCallback::OnSessionCreated() + // with the web session ID on success, or OnSessionError() on failure, + // and then call OnSessionReady() once all keys for that session are + // available. + virtual void CreateSession(uint32_t aPromiseId, + const char* aInitDataType, + uint32_t aInitDataTypeSize, + const uint8_t* aInitData, + uint32_t aInitDataSize, + GMPSessionType aSessionType) = 0; + + // Loads a previously loaded persistent session. + virtual void LoadSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) = 0; + + // Updates the session with |aResponse|. + virtual void UpdateSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aResponse, + uint32_t aResponseSize) = 0; + + // Releases the resources (keys) for the specified session. + virtual void CloseSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) = 0; + + // Removes the resources (keys) for the specified session. + virtual void RemoveSession(uint32_t aPromiseId, + const char* aSessionId, + uint32_t aSessionIdLength) = 0; + + // Resolve/reject promise on completion. + virtual void SetServerCertificate(uint32_t aPromiseId, + const uint8_t* aServerCert, + uint32_t aServerCertSize) = 0; +}; + +#endif // GMP_DECRYPTION_h_
--- a/content/media/gmp/gmp-api/gmp-errors.h +++ b/content/media/gmp/gmp-api/gmp-errors.h @@ -30,12 +30,21 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GMP_ERRORS_h_ #define GMP_ERRORS_h_ typedef enum { GMPNoErr = 0, - GMPGenericErr = 1 + GMPGenericErr = 1, + GMPClosedErr = 2, + GMPAllocErr = 3, + GMPNotImplementedErr = 4, + GMPNotClosedErr = 5, + GMPQuotaExceededErr = 6, + GMPLastErr // Placeholder, must be last. This enum's values must remain consecutive! } GMPErr; +#define GMP_SUCCEEDED(x) ((x) == GMPNoErr) +#define GMP_FAILED(x) ((x) != GMPNoErr) + #endif // GMP_ERRORS_h_
--- a/content/media/gmp/gmp-api/gmp-platform.h +++ b/content/media/gmp/gmp-api/gmp-platform.h @@ -29,22 +29,24 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GMP_PLATFORM_h_ #define GMP_PLATFORM_h_ #include "gmp-errors.h" +#include "gmp-storage.h" #include <stdint.h> /* Platform helper API. */ class GMPTask { public: + virtual void Destroy() = 0; virtual ~GMPTask() {} virtual void Run() = 0; }; class GMPThread { public: virtual ~GMPThread() {} virtual void Post(GMPTask* aTask) = 0; @@ -53,27 +55,40 @@ public: class GMPMutex { public: virtual ~GMPMutex() {} virtual void Acquire() = 0; virtual void Release() = 0; }; +// Time is defined as the number of milliseconds since the +// Epoch (00:00:00 UTC, January 1, 1970). +typedef int64_t GMPTimestamp; + typedef GMPErr (*GMPCreateThreadPtr)(GMPThread** aThread); typedef GMPErr (*GMPRunOnMainThreadPtr)(GMPTask* aTask); typedef GMPErr (*GMPSyncRunOnMainThreadPtr)(GMPTask* aTask); typedef GMPErr (*GMPCreateMutexPtr)(GMPMutex** aMutex); +typedef GMPErr (*GMPCreateRecordPtr)(const char* aRecordName, + uint32_t aRecordNameSize, + GMPRecord** aOutRecord, + GMPRecordClient* aClient); +typedef GMPErr (*GMPSetTimerOnMainThreadPtr)(GMPTask* aTask, int64_t aTimeoutMS); +typedef GMPErr (*GMPGetCurrentTimePtr)(GMPTimestamp* aOutTime); struct GMPPlatformAPI { // Increment the version when things change. Can only add to the struct, // do not change what already exists. Pointers to functions may be NULL // when passed to plugins, but beware backwards compat implications of // doing that. uint16_t version; // Currently version 0 GMPCreateThreadPtr createthread; GMPRunOnMainThreadPtr runonmainthread; GMPSyncRunOnMainThreadPtr syncrunonmainthread; GMPCreateMutexPtr createmutex; + GMPCreateRecordPtr createrecord; + GMPSetTimerOnMainThreadPtr settimer; + GMPGetCurrentTimePtr getcurrenttime; }; #endif // GMP_PLATFORM_h_
new file mode 100644 --- /dev/null +++ b/content/media/gmp/gmp-api/gmp-storage.h @@ -0,0 +1,90 @@ +/* +* Copyright 2013, Mozilla Foundation and contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef GMP_STORAGE_h_ +#define GMP_STORAGE_h_ + +#include "gmp-errors.h" +#include <stdint.h> + +// Provides basic per-origin storage for CDMs. GMPRecord instances can be +// retrieved by calling GMPPlatformAPI->openstorage. Multiple GMPRecord +// can be open at once. This interface is asynchronous, with results +// being returned via callbacks to the GMPRecordClient pointer provided +// to the GMPPlatformAPI->openstorage call, on the main thread. +class GMPRecord { +public: + + // Opens the record. Calls OnOpenComplete() once the record is open. + // Note: OnReadComplete() is only called if this returns GMPNoErr. + virtual GMPErr Open() = 0; + + // Reads the entire contents of the file, and calls + // GMPRecordClient::OnReadComplete() once the operation is complete. + // Note: OnReadComplete() is only called if this returns GMPNoErr. + virtual GMPErr Read() = 0; + + // Writes aDataSize bytes of aData into the file, overwritting the contents + // of the file. Overwriting with 0 bytes "deletes" the file. + // Write 0 bytes to "delete" a file. + // Note: OnWriteComplete is only called if this returns GMPNoErr. + virtual GMPErr Write(const uint8_t* aData, uint32_t aDataSize) = 0; + + // Closes a file. File must not be used after this is called. Cancels all + // callbacks. + virtual GMPErr Close() = 0; + + virtual ~GMPRecord() {} +}; + +// Callback object that receives the results of GMPRecord calls. Callbacks +// run asynchronously to the GMPRecord call, on the main thread. +class GMPRecordClient { + public: + + // Response to a GMPRecord::Open() call with the open |status|. + // aStatus values: + // - GMPNoErr - File opened successfully. File may be empty. + // - GMPFileInUse - There file is in use by another client. + // - GMPGenericErr - Unspecified error. + // Do not use the GMPRecord if aStatus is not GMPNoErr. + virtual void OnOpenComplete(GMPErr aStatus) = 0; + + // Response to a GMPRecord::Read() call, where aData is the file contents, + // of length aDataSize. + // aData is only valid for the duration of the call to OnReadComplete. + // Copy it if you want to hang onto it! + // aStatus values: + // - GMPNoErr - File contents read successfully, aDataSize 0 means file + // is empty. + // - GMPFileInUse - There are other operations or clients in use on this file. + // - GMPGenericErr - Unspecified error. + // Do not continue to use the GMPRecord if aStatus is not GMPNoErr. + virtual void OnReadComplete(GMPErr aStatus, + const uint8_t* aData, + uint32_t aDataSize) = 0; + + // Response to a GMPRecord::Write() call. + // - GMPNoErr - File contents written successfully. + // - GMPFileInUse - There are other operations or clients in use on this file. + // - GMPGenericErr - Unspecified error. File should be regarded as corrupt. + // Do not continue to use the GMPRecord if aStatus is not GMPNoErr. + virtual void OnWriteComplete(GMPErr aStatus) = 0; + + virtual ~GMPRecordClient() {} +}; + +#endif // GMP_STORAGE_h_
--- a/content/media/gmp/gmp-api/gmp-video-codec.h +++ b/content/media/gmp/gmp-api/gmp-video-codec.h @@ -67,83 +67,103 @@ struct GMPVideoCodecVP8 bool mPictureLossIndicationOn; bool mFeedbackModeOn; GMPVideoCodecComplexity mComplexity; GMPVP8ResilienceMode mResilience; uint32_t mNumberOfTemporalLayers; bool mDenoisingOn; bool mErrorConcealmentOn; bool mAutomaticResizeOn; - bool mFrameDroppingOn; - int32_t mKeyFrameInterval; }; // H264 specific -struct GMPVideoCodecH264 + +// Needs to match a binary spec for this structure. +// Note: the mSPS at the end of this structure is variable length. +struct GMPVideoCodecH264AVCC { - uint8_t mProfile; + uint8_t mVersion; // == 0x01 + uint8_t mProfile; // these 3 are profile_level_id uint8_t mConstraints; uint8_t mLevel; + uint8_t mLengthSizeMinusOne; // lower 2 bits (== GMPBufferType-1). Top 6 reserved (1's) + + // SPS/PPS will not generally be present for interactive use unless SDP + // parameter-sets are used. + uint8_t mNumSPS; // lower 5 bits; top 5 reserved (1's) + + /*** uint8_t mSPS[]; (Not defined due to compiler warnings and warnings-as-errors ...) **/ + // Following mNumSPS is a variable number of bytes, which is the SPS and PPS. + // Each SPS == 16 bit size, ("N"), then "N" bytes, + // then uint8_t mNumPPS, then each PPS == 16 bit size ("N"), then "N" bytes. +}; + +// Codec specific data for H.264 decoding/encoding. +// Cast the "aCodecSpecific" parameter of GMPVideoDecoder::InitDecode() and +// GMPVideoEncoder::InitEncode() to this structure. +struct GMPVideoCodecH264 +{ uint8_t mPacketizationMode; // 0 or 1 - bool mFrameDroppingOn; - int32_t mKeyFrameInterval; - // These are null/0 if not externally negotiated - const uint8_t* mSPSData; - size_t mSPSLen; - const uint8_t* mPPSData; - size_t mPPSLen; + struct GMPVideoCodecH264AVCC mAVCC; // holds a variable-sized struct GMPVideoCodecH264AVCC mAVCC; }; enum GMPVideoCodecType { kGMPVideoCodecVP8, + + // Encoded frames are in AVCC format; NAL length field of 4 bytes, followed + // by frame data. May be multiple NALUs per sample. Codec specific extra data + // is the AVCC extra data (in AVCC format). kGMPVideoCodecH264, kGMPVideoCodecInvalid // Should always be last. }; -union GMPVideoCodecUnion -{ - GMPVideoCodecVP8 mVP8; - GMPVideoCodecH264 mH264; -}; - // Simulcast is when the same stream is encoded multiple times with different // settings such as resolution. struct GMPSimulcastStream { uint32_t mWidth; uint32_t mHeight; uint32_t mNumberOfTemporalLayers; uint32_t mMaxBitrate; // kilobits/sec. uint32_t mTargetBitrate; // kilobits/sec. uint32_t mMinBitrate; // kilobits/sec. uint32_t mQPMax; // minimum quality }; enum GMPVideoCodecMode { kGMPRealtimeVideo, kGMPScreensharing, + kGMPStreamingVideo, kGMPCodecModeInvalid // Should always be last. }; +enum GMPApiVersion { + kGMPVersion32 = 1, // leveraging that V32 had mCodecType first, and only supported H264 + kGMPVersion33 = 33, +}; + struct GMPVideoCodec { + uint32_t mGMPApiVersion; + GMPVideoCodecType mCodecType; char mPLName[kGMPPayloadNameSize]; // Must be NULL-terminated! uint32_t mPLType; uint32_t mWidth; uint32_t mHeight; uint32_t mStartBitrate; // kilobits/sec. uint32_t mMaxBitrate; // kilobits/sec. uint32_t mMinBitrate; // kilobits/sec. uint32_t mMaxFramerate; - GMPVideoCodecUnion mCodecSpecific; + bool mFrameDroppingOn; + int32_t mKeyFrameInterval; uint32_t mQPMax; uint32_t mNumberOfSimulcastStreams; GMPSimulcastStream mSimulcastStream[kGMPMaxSimulcastStreams]; GMPVideoCodecMode mMode; }; @@ -152,16 +172,17 @@ struct GMPVideoCodec // entry; one should check the overall end-of-buffer against where the next // length would be. enum GMPBufferType { GMP_BufferSingle = 0, GMP_BufferLength8, GMP_BufferLength16, GMP_BufferLength24, GMP_BufferLength32, + GMP_BufferInvalid, }; struct GMPCodecSpecificInfoGeneric { uint8_t mSimulcastIdx; }; struct GMPCodecSpecificInfoH264 { uint8_t mSimulcastIdx; @@ -183,16 +204,17 @@ struct GMPCodecSpecificInfoVP8 int32_t mTL0PicIdx; // negative value to skip tl0PicIdx int8_t mKeyIdx; // negative value to skip keyIdx }; union GMPCodecSpecificInfoUnion { GMPCodecSpecificInfoGeneric mGeneric; GMPCodecSpecificInfoVP8 mVP8; + GMPCodecSpecificInfoH264 mH264; }; // Note: if any pointers are added to this struct or its sub-structs, it // must be fitted with a copy-constructor. This is because it is copied // in the copy-constructor of VCMEncodedFrame. struct GMPCodecSpecificInfo { GMPVideoCodecType mCodecType;
--- a/content/media/gmp/gmp-api/gmp-video-decode.h +++ b/content/media/gmp/gmp-api/gmp-video-decode.h @@ -29,72 +29,87 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GMP_VIDEO_DECODE_h_ #define GMP_VIDEO_DECODE_h_ -#include "gmp-video-errors.h" +#include "gmp-errors.h" #include "gmp-video-frame-i420.h" #include "gmp-video-frame-encoded.h" #include "gmp-video-codec.h" #include <stdint.h> // ALL METHODS MUST BE CALLED ON THE MAIN THREAD -class GMPDecoderCallback +class GMPVideoDecoderCallback { public: - virtual ~GMPDecoderCallback() {} + virtual ~GMPVideoDecoderCallback() {} virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) = 0; virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) = 0; virtual void ReceivedDecodedFrame(const uint64_t aPictureId) = 0; virtual void InputDataExhausted() = 0; + + virtual void DrainComplete() = 0; + + virtual void ResetComplete() = 0; }; // ALL METHODS MUST BE CALLED ON THE MAIN THREAD class GMPVideoDecoder { public: virtual ~GMPVideoDecoder() {} - // aCallback: Subclass should retain reference to it until DecodingComplete - // is called. Do not attempt to delete it, host retains ownership. - virtual GMPVideoErr InitDecode(const GMPVideoCodec& aCodecSettings, - GMPDecoderCallback* aCallback, - int32_t aCoreCount) = 0; + // - aCodecSettings: Details of decoder to create. + // - aCodecSpecific: codec specific data, cast to a GMPVideoCodecXXX struct + // to get codec specific config data. + // - aCodecSpecificLength: number of bytes in aCodecSpecific. + // - aCallback: Subclass should retain reference to it until DecodingComplete + // is called. Do not attempt to delete it, host retains ownership. + // aCoreCount: number of CPU cores. + virtual GMPErr InitDecode(const GMPVideoCodec& aCodecSettings, + const uint8_t* aCodecSpecific, + uint32_t aCodecSpecificLength, + GMPVideoDecoderCallback* aCallback, + int32_t aCoreCount) = 0; // Decode encoded frame (as a part of a video stream). The decoded frame // will be returned to the user through the decode complete callback. // - // inputFrame: Frame to decode. - // - // missingFrames: True if one or more frames have been lost since the previous decode call. - // - // fragmentation: Specifies where the encoded frame can be split into separate fragments. - // The meaning of fragment is codec specific, but often means that each - // fragment is decodable by itself. - // - // codecSpecificInfo: Codec-specific data - // - // renderTimeMs : System time to render in milliseconds. Only used by decoders with internal - // rendering. - virtual GMPVideoErr Decode(GMPVideoEncodedFrame* aInputFrame, - bool aMissingFrames, - const GMPCodecSpecificInfo& aCodecSpecificInfo, - int64_t aRenderTimeMs = -1) = 0; + // - aInputFrame: Frame to decode. Call Destroy() on frame when it's decoded. + // - aMissingFrames: True if one or more frames have been lost since the + // previous decode call. + // - aCodecSpecificInfo : codec specific data, pointer to a + // GMPCodecSpecificInfo structure appropriate for + // this codec type. + // - aCodecSpecificInfoLength : number of bytes in aCodecSpecificInfo + // - renderTimeMs : System time to render in milliseconds. Only used by + // decoders with internal rendering. + virtual GMPErr Decode(GMPVideoEncodedFrame* aInputFrame, + bool aMissingFrames, + const uint8_t* aCodecSpecificInfo, + uint32_t aCodecSpecificInfoLength, + int64_t aRenderTimeMs = -1) = 0; - // Reset decoder state and prepare for a new call to Decode(...). Flushes the decoder pipeline. - virtual GMPVideoErr Reset() = 0; + // Reset decoder state and prepare for a new call to Decode(...). + // Flushes the decoder pipeline. + // The decoder should enqueue a task to run ResetComplete() on the main + // thread once the reset has finished. + virtual GMPErr Reset() = 0; // Output decoded frames for any data in the pipeline, regardless of ordering. - virtual GMPVideoErr Drain() = 0; + // All remaining decoded frames should be immediately returned via callback. + // The decoder should enqueue a task to run DrainComplete() on the main + // thread once the reset has finished. + virtual GMPErr Drain() = 0; // May free decoder memory. virtual void DecodingComplete() = 0; }; #endif // GMP_VIDEO_DECODE_h_
--- a/content/media/gmp/gmp-api/gmp-video-encode.h +++ b/content/media/gmp/gmp-api/gmp-video-encode.h @@ -32,79 +32,92 @@ */ #ifndef GMP_VIDEO_ENCODE_h_ #define GMP_VIDEO_ENCODE_h_ #include <vector> #include <stdint.h> -#include "gmp-video-errors.h" +#include "gmp-errors.h" #include "gmp-video-frame-i420.h" #include "gmp-video-frame-encoded.h" #include "gmp-video-codec.h" // ALL METHODS MUST BE CALLED ON THE MAIN THREAD -class GMPEncoderCallback +class GMPVideoEncoderCallback { public: - virtual ~GMPEncoderCallback() {} + virtual ~GMPVideoEncoderCallback() {} virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo) = 0; + const uint8_t* aCodecSpecificInfo, + uint32_t aCodecSpecificInfoLength) = 0; }; // ALL METHODS MUST BE CALLED ON THE MAIN THREAD class GMPVideoEncoder { public: virtual ~GMPVideoEncoder() {} // Initialize the encoder with the information from the VideoCodec. // // Input: // - codecSettings : Codec settings + // - aCodecSpecific : codec specific data, pointer to a + // GMPCodecSpecific structure appropriate for + // this codec type. + // - aCodecSpecificLength : number of bytes in aCodecSpecific // - aCallback: Subclass should retain reference to it until EncodingComplete // is called. Do not attempt to delete it, host retains ownership. - // - numberOfCores : Number of cores available for the encoder - // - maxPayloadSize : The maximum size each payload is allowed + // - aNnumberOfCores : Number of cores available for the encoder + // - aMaxPayloadSize : The maximum size each payload is allowed // to have. Usually MTU - overhead. - virtual GMPVideoErr InitEncode(const GMPVideoCodec& aCodecSettings, - GMPEncoderCallback* aCallback, - int32_t aNumberOfCores, - uint32_t aMaxPayloadSize) = 0; + virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings, + const uint8_t* aCodecSpecific, + uint32_t aCodecSpecificLength, + GMPVideoEncoderCallback* aCallback, + int32_t aNumberOfCores, + uint32_t aMaxPayloadSize) = 0; // Encode an I420 frame (as a part of a video stream). The encoded frame // will be returned to the user through the encode complete callback. // // Input: - // - inputFrame : Frame to be encoded - // - codecSpecificInfo : Pointer to codec specific data - // - frame_types : The frame type to encode - virtual GMPVideoErr Encode(GMPVideoi420Frame* aInputFrame, - const GMPCodecSpecificInfo& aCodecSpecificInfo, - const std::vector<GMPVideoFrameType>& aFrameTypes) = 0; + // - aInputFrame : Frame to be encoded + // - aCodecSpecificInfo : codec specific data, pointer to a + // GMPCodecSpecificInfo structure appropriate for + // this codec type. + // - aCodecSpecificInfoLength : number of bytes in aCodecSpecific + // - aFrameTypes : The frame type to encode + // - aFrameTypesLength : The number of elements in aFrameTypes array. + virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame, + const uint8_t* aCodecSpecificInfo, + uint32_t aCodecSpecificInfoLength, + const GMPVideoFrameType* aFrameTypes, + uint32_t aFrameTypesLength) = 0; // Inform the encoder about the packet loss and round trip time on the // network used to decide the best pattern and signaling. // // - packetLoss : Fraction lost (loss rate in percent = // 100 * packetLoss / 255) // - rtt : Round-trip time in milliseconds - virtual GMPVideoErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0; + virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0; // Inform the encoder about the new target bit rate. // // - newBitRate : New target bit rate // - frameRate : The target frame rate - virtual GMPVideoErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0; + virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0; // Use this function to enable or disable periodic key frames. Can be useful for codecs // which have other ways of stopping error propagation. // // - enable : Enable or disable periodic key frames - virtual GMPVideoErr SetPeriodicKeyFrames(bool aEnable) = 0; + virtual GMPErr SetPeriodicKeyFrames(bool aEnable) = 0; // May free Encoder memory. virtual void EncodingComplete() = 0; }; #endif // GMP_VIDEO_ENCODE_h_
deleted file mode 100644 --- a/content/media/gmp/gmp-api/gmp-video-errors.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* Copyright (c) 2011, The WebRTC project authors. All rights reserved. - * Copyright (c) 2014, Mozilla - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - ** Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - ** Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - ** Neither the name of Google nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef GMP_VIDEO_ERRORS_h_ -#define GMP_VIDEO_ERRORS_h_ - -enum GMPVideoErr { - GMPVideoNoErr = 0, - GMPVideoGenericErr = 1, - GMPVideoAllocErr = 2 -}; - -#endif // GMP_VIDEO_ERRORS_h_
--- a/content/media/gmp/gmp-api/gmp-video-frame-encoded.h +++ b/content/media/gmp/gmp-api/gmp-video-frame-encoded.h @@ -30,16 +30,19 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GMP_VIDEO_FRAME_ENCODED_h_ #define GMP_VIDEO_FRAME_ENCODED_h_ #include <stdint.h> +#include "gmp-decryption.h" +#include "gmp-video-frame.h" +#include "gmp-video-codec.h" enum GMPVideoFrameType { kGMPKeyFrame = 0, kGMPDeltaFrame = 1, kGMPGoldenFrame = 2, kGMPAltRefFrame = 3, kGMPSkipFrame = 4 @@ -53,32 +56,43 @@ enum GMPVideoFrameType // access the underlying buffer(s). // // Methods that create or destroy shared memory must be called on the main // thread. They are marked below. class GMPVideoEncodedFrame : public GMPVideoFrame { public: // MAIN THREAD ONLY - virtual GMPVideoErr CreateEmptyFrame(uint32_t aSize) = 0; + virtual GMPErr CreateEmptyFrame(uint32_t aSize) = 0; // MAIN THREAD ONLY - virtual GMPVideoErr CopyFrame(const GMPVideoEncodedFrame& aVideoFrame) = 0; + virtual GMPErr CopyFrame(const GMPVideoEncodedFrame& aVideoFrame) = 0; virtual void SetEncodedWidth(uint32_t aEncodedWidth) = 0; virtual uint32_t EncodedWidth() = 0; virtual void SetEncodedHeight(uint32_t aEncodedHeight) = 0; virtual uint32_t EncodedHeight() = 0; - virtual void SetTimeStamp(uint32_t aTimeStamp) = 0; - virtual uint32_t TimeStamp() = 0; - virtual void SetCaptureTime(int64_t aCaptureTime) = 0; - virtual int64_t CaptureTime() = 0; + // Microseconds + virtual void SetTimeStamp(uint64_t aTimeStamp) = 0; + virtual uint64_t TimeStamp() = 0; + // Set frame duration (microseconds) + // NOTE: next-frame's Timestamp() != this-frame's TimeStamp()+Duration() + // depending on rounding to avoid having to track roundoff errors + // and dropped/missing frames(!) (which may leave a large gap) + virtual void SetDuration(uint64_t aDuration) = 0; + virtual uint64_t Duration() const = 0; virtual void SetFrameType(GMPVideoFrameType aFrameType) = 0; virtual GMPVideoFrameType FrameType() = 0; virtual void SetAllocatedSize(uint32_t aNewSize) = 0; virtual uint32_t AllocatedSize() = 0; virtual void SetSize(uint32_t aSize) = 0; virtual uint32_t Size() = 0; virtual void SetCompleteFrame(bool aCompleteFrame) = 0; virtual bool CompleteFrame() = 0; virtual const uint8_t* Buffer() const = 0; virtual uint8_t* Buffer() = 0; + virtual GMPBufferType BufferType() const = 0; + virtual void SetBufferType(GMPBufferType aBufferType) = 0; + + // Get data describing how this frame is encrypted, or nullptr if the + // frame is not encrypted. + virtual const GMPEncryptedBufferData* GetDecryptionData() const = 0; }; #endif // GMP_VIDEO_FRAME_ENCODED_h_
--- a/content/media/gmp/gmp-api/gmp-video-frame-i420.h +++ b/content/media/gmp/gmp-api/gmp-video-frame-i420.h @@ -29,17 +29,17 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GMP_VIDEO_FRAME_I420_h_ #define GMP_VIDEO_FRAME_I420_h_ -#include "gmp-video-errors.h" +#include "gmp-errors.h" #include "gmp-video-frame.h" #include "gmp-video-plane.h" #include <stdint.h> enum GMPPlaneType { kGMPYPlane = 0, kGMPUPlane = 1, @@ -58,32 +58,32 @@ enum GMPPlaneType { // thread. They are marked below. class GMPVideoi420Frame : public GMPVideoFrame { public: // MAIN THREAD ONLY // CreateEmptyFrame: Sets frame dimensions and allocates buffers based // on set dimensions - height and plane stride. // If required size is bigger than the allocated one, new buffers of adequate // size will be allocated. - virtual GMPVideoErr CreateEmptyFrame(int32_t aWidth, int32_t aHeight, - int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0; + virtual GMPErr CreateEmptyFrame(int32_t aWidth, int32_t aHeight, + int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0; // MAIN THREAD ONLY // CreateFrame: Sets the frame's members and buffers. If required size is // bigger than allocated one, new buffers of adequate size will be allocated. - virtual GMPVideoErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y, - int32_t aSize_u, const uint8_t* aBuffer_u, - int32_t aSize_v, const uint8_t* aBuffer_v, - int32_t aWidth, int32_t aHeight, - int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0; + virtual GMPErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y, + int32_t aSize_u, const uint8_t* aBuffer_u, + int32_t aSize_v, const uint8_t* aBuffer_v, + int32_t aWidth, int32_t aHeight, + int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0; // MAIN THREAD ONLY // Copy frame: If required size is bigger than allocated one, new buffers of // adequate size will be allocated. - virtual GMPVideoErr CopyFrame(const GMPVideoi420Frame& aVideoFrame) = 0; + virtual GMPErr CopyFrame(const GMPVideoi420Frame& aVideoFrame) = 0; // Swap Frame. virtual void SwapFrame(GMPVideoi420Frame* aVideoFrame) = 0; // Get pointer to buffer per plane. virtual uint8_t* Buffer(GMPPlaneType aType) = 0; // Overloading with const. @@ -91,38 +91,41 @@ public: // Get allocated size per plane. virtual int32_t AllocatedSize(GMPPlaneType aType) const = 0; // Get allocated stride per plane. virtual int32_t Stride(GMPPlaneType aType) const = 0; // Set frame width. - virtual GMPVideoErr SetWidth(int32_t aWidth) = 0; + virtual GMPErr SetWidth(int32_t aWidth) = 0; // Set frame height. - virtual GMPVideoErr SetHeight(int32_t aHeight) = 0; + virtual GMPErr SetHeight(int32_t aHeight) = 0; // Get frame width. virtual int32_t Width() const = 0; // Get frame height. virtual int32_t Height() const = 0; - // Set frame timestamp (90kHz). - virtual void SetTimestamp(uint32_t aTimestamp) = 0; + // Set frame timestamp (microseconds) + virtual void SetTimestamp(uint64_t aTimestamp) = 0; - // Get frame timestamp (90kHz). - virtual uint32_t Timestamp() const = 0; + // Get frame timestamp (microseconds) + virtual uint64_t Timestamp() const = 0; - // Set render time in miliseconds. - virtual void SetRenderTime_ms(int64_t aRenderTime_ms) = 0; + // Set frame duration (microseconds) + // NOTE: next-frame's Timestamp() != this-frame's TimeStamp()+Duration() + // depending on rounding to avoid having to track roundoff errors + // and dropped/missing frames(!) (which may leave a large gap) + virtual void SetDuration(uint64_t aDuration) = 0; - // Get render time in miliseconds. - virtual int64_t RenderTime_ms() const = 0; + // Get frame duration (microseconds) + virtual uint64_t Duration() const = 0; // Return true if underlying plane buffers are of zero size, false if not. virtual bool IsZeroSize() const = 0; // Reset underlying plane buffers sizes to 0. This function doesn't clear memory. virtual void ResetSize() = 0; };
--- a/content/media/gmp/gmp-api/gmp-video-frame.h +++ b/content/media/gmp/gmp-api/gmp-video-frame.h @@ -29,17 +29,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GMP_VIDEO_FRAME_h_ #define GMP_VIDEO_FRAME_h_ -#include "gmp-video-errors.h" #include "gmp-video-plane.h" enum GMPVideoFrameFormat { kGMPEncodedVideoFrame = 0, kGMPI420VideoFrame = 1 }; class GMPVideoFrame {
--- a/content/media/gmp/gmp-api/gmp-video-host.h +++ b/content/media/gmp/gmp-api/gmp-video-host.h @@ -29,23 +29,23 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GMP_VIDEO_HOST_h_ #define GMP_VIDEO_HOST_h_ -#include "gmp-video-errors.h" +#include "gmp-errors.h" #include "gmp-video-frame-i420.h" #include "gmp-video-frame-encoded.h" #include "gmp-video-codec.h" class GMPVideoHost { public: // Construct various video API objects. Host does not retain reference, // caller is owner and responsible for deleting. - virtual GMPVideoErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) = 0; - virtual GMPVideoErr CreatePlane(GMPPlane** aPlane) = 0; + virtual GMPErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) = 0; + virtual GMPErr CreatePlane(GMPPlane** aPlane) = 0; }; #endif // GMP_VIDEO_HOST_h_
--- a/content/media/gmp/gmp-api/gmp-video-plane.h +++ b/content/media/gmp/gmp-api/gmp-video-plane.h @@ -29,17 +29,17 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GMP_VIDEO_PLANE_h_ #define GMP_VIDEO_PLANE_h_ -#include "gmp-video-errors.h" +#include "gmp-errors.h" #include <stdint.h> // The implementation backing this interface uses shared memory for the // buffer(s). This means it can only be used by the "owning" process. // At first the process which created the object owns it. When the object // is passed to an interface the creator loses ownership and must Destroy() // the object. Further attempts to use it may fail due to not being able to // access the underlying buffer(s). @@ -47,28 +47,28 @@ // Methods that create or destroy shared memory must be called on the main // thread. They are marked below. class GMPPlane { public: // MAIN THREAD ONLY // CreateEmptyPlane - set allocated size, actual plane size and stride: // If current size is smaller than current size, then a buffer of sufficient // size will be allocated. - virtual GMPVideoErr CreateEmptyPlane(int32_t aAllocatedSize, - int32_t aStride, - int32_t aPlaneSize) = 0; + virtual GMPErr CreateEmptyPlane(int32_t aAllocatedSize, + int32_t aStride, + int32_t aPlaneSize) = 0; // MAIN THREAD ONLY // Copy the entire plane data. - virtual GMPVideoErr Copy(const GMPPlane& aPlane) = 0; + virtual GMPErr Copy(const GMPPlane& aPlane) = 0; // MAIN THREAD ONLY // Copy buffer: If current size is smaller // than current size, then a buffer of sufficient size will be allocated. - virtual GMPVideoErr Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer) = 0; + virtual GMPErr Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer) = 0; // Swap plane data. virtual void Swap(GMPPlane& aPlane) = 0; // Get allocated size. virtual int32_t AllocatedSize() const = 0; // Set actual size.
--- a/content/media/gmp/moz.build +++ b/content/media/gmp/moz.build @@ -6,53 +6,62 @@ XPIDL_MODULE = 'content_geckomediaplugins' XPIDL_SOURCES += [ 'mozIGeckoMediaPluginService.idl', ] EXPORTS += [ + 'gmp-api/gmp-async-shutdown.h', + 'gmp-api/gmp-audio-codec.h', + 'gmp-api/gmp-audio-decode.h', + 'gmp-api/gmp-audio-host.h', + 'gmp-api/gmp-audio-samples.h', + 'gmp-api/gmp-decryption.h', 'gmp-api/gmp-entrypoints.h', 'gmp-api/gmp-errors.h', 'gmp-api/gmp-platform.h', + 'gmp-api/gmp-storage.h', 'gmp-api/gmp-video-codec.h', 'gmp-api/gmp-video-decode.h', 'gmp-api/gmp-video-encode.h', - 'gmp-api/gmp-video-errors.h', 'gmp-api/gmp-video-frame-encoded.h', 'gmp-api/gmp-video-frame-i420.h', 'gmp-api/gmp-video-frame.h', 'gmp-api/gmp-video-host.h', 'gmp-api/gmp-video-plane.h', 'GMPChild.h', 'GMPMessageUtils.h', 'GMPParent.h', 'GMPPlatform.h', 'GMPProcessChild.h', 'GMPProcessParent.h', 'GMPService.h', 'GMPSharedMemManager.h', 'GMPVideoDecoderChild.h', 'GMPVideoDecoderParent.h', + 'GMPVideoDecoderProxy.h', 'GMPVideoEncodedFrameImpl.h', 'GMPVideoEncoderChild.h', 'GMPVideoEncoderParent.h', + 'GMPVideoEncoderProxy.h', 'GMPVideoHost.h', 'GMPVideoi420FrameImpl.h', 'GMPVideoPlaneImpl.h', ] UNIFIED_SOURCES += [ 'GMPChild.cpp', 'GMPParent.cpp', 'GMPPlatform.cpp', 'GMPProcessChild.cpp', 'GMPProcessParent.cpp', 'GMPService.cpp', + 'GMPSharedMemManager.cpp', 'GMPVideoDecoderChild.cpp', 'GMPVideoDecoderParent.cpp', 'GMPVideoEncodedFrameImpl.cpp', 'GMPVideoEncoderChild.cpp', 'GMPVideoEncoderParent.cpp', 'GMPVideoHost.cpp', 'GMPVideoi420FrameImpl.cpp', 'GMPVideoPlaneImpl.cpp', @@ -65,16 +74,19 @@ IPDL_SOURCES += [ 'PGMPVideoEncoder.ipdl', ] LIBRARY_NAME = 'mozgmp' if CONFIG['GKMEDIAS_SHARED_LIBRARY']: NO_VISIBILITY_FLAGS = True +# comment this out to use Unsafe Shmem for more performance +DEFINES['GMP_SAFE_SHMEM'] = True + FAIL_ON_WARNINGS = True include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '../base', '/xpcom/base',
--- a/content/media/gmp/mozIGeckoMediaPluginService.idl +++ b/content/media/gmp/mozIGeckoMediaPluginService.idl @@ -5,56 +5,56 @@ #include "nsISupports.idl" #include "nsIThread.idl" #include "nsIPrincipal.idl" %{C++ #include "nsTArray.h" #include "nsStringGlue.h" -class GMPVideoDecoder; -class GMPVideoEncoder; +class GMPVideoDecoderProxy; +class GMPVideoEncoderProxy; class GMPVideoHost; %} -[ptr] native GMPVideoDecoder(GMPVideoDecoder); -[ptr] native GMPVideoEncoder(GMPVideoEncoder); +[ptr] native GMPVideoDecoderProxy(GMPVideoDecoderProxy); +[ptr] native GMPVideoEncoderProxy(GMPVideoEncoderProxy); [ptr] native GMPVideoHost(GMPVideoHost); [ptr] native MessageLoop(MessageLoop); [ptr] native TagArray(nsTArray<nsCString>); -[scriptable, uuid(63fc797f-9d01-43f4-8b93-5b1fe713c2f8)] +[scriptable, uuid(7cef50ca-7a0f-41f2-9560-47abf709f0d7)] interface mozIGeckoMediaPluginService : nsISupports { /** * The GMP thread. Callable from any thread. */ readonly attribute nsIThread thread; /** * Get a video decoder that supports the specified tags. * The array of tags should at least contain a codec tag, and optionally * other tags such as for EME keysystem. * Callable only on GMP thread. */ [noscript] - GMPVideoDecoder getGMPVideoDecoder(in TagArray tags, - [optional] in AString origin, - out GMPVideoHost outVideoHost); + GMPVideoDecoderProxy getGMPVideoDecoder(in TagArray tags, + [optional] in AString origin, + out GMPVideoHost outVideoHost); /** * Get a video encoder that supports the specified tags. * The array of tags should at least contain a codec tag, and optionally * other tags. * Callable only on GMP thread. */ [noscript] - GMPVideoEncoder getGMPVideoEncoder(in TagArray tags, - [optional] in AString origin, - out GMPVideoHost outVideoHost); + GMPVideoEncoderProxy getGMPVideoEncoder(in TagArray tags, + [optional] in AString origin, + out GMPVideoHost outVideoHost); /** * Add a directory to scan for gecko media plugins. * @note Main-thread API. */ void addPluginDirectory(in AString directory); /**
--- a/content/media/gtest/moz.build +++ b/content/media/gtest/moz.build @@ -13,18 +13,16 @@ UNIFIED_SOURCES += [ ] if CONFIG['MOZ_WEBM_ENCODER']: UNIFIED_SOURCES += ['TestVideoTrackEncoder.cpp', 'TestVorbisTrackEncoder.cpp', 'TestWebMWriter.cpp', ] -EXPORT_LIBRARY = True - include('/ipc/chromium/chromium-config.mozbuild') LOCAL_INCLUDES += [ '/content/media/encoder', ] FINAL_LIBRARY = 'xul-gtest'
--- a/content/media/moz.build +++ b/content/media/moz.build @@ -139,16 +139,17 @@ UNIFIED_SOURCES += [ 'DOMMediaStream.cpp', 'EncodedBufferCache.cpp', 'FileBlockCache.cpp', 'MediaCache.cpp', 'MediaData.cpp', 'MediaDecoder.cpp', 'MediaDecoderReader.cpp', 'MediaDecoderStateMachine.cpp', + 'MediaDecoderStateMachineScheduler.cpp', 'MediaRecorder.cpp', 'MediaResource.cpp', 'MediaShutdownManager.cpp', 'MediaStreamGraph.cpp', 'MediaStreamTrack.cpp', 'MediaTaskQueue.cpp', 'MediaTrack.cpp', 'MediaTrackList.cpp',
--- a/content/media/test/mochitest.ini +++ b/content/media/test/mochitest.ini @@ -467,9 +467,8 @@ skip-if = webm [test_can_play_type_wave.html] run-if = wave [test_can_play_type_no_wave.html] skip-if = wave [test_fragment_noplay.html] run-if = wave [test_fragment_play.html] run-if = wave -skip-if = buildapp == 'b2g' || toolkit == 'android' # Intermittent failures - Bug 996465 & bug 924246
--- a/content/svg/content/src/SVGMatrix.cpp +++ b/content/svg/content/src/SVGMatrix.cpp @@ -114,21 +114,22 @@ SVGMatrix::Multiply(SVGMatrix& aMatrix) { nsRefPtr<SVGMatrix> matrix = new SVGMatrix(aMatrix.GetMatrix() * GetMatrix()); return matrix.forget(); } already_AddRefed<SVGMatrix> SVGMatrix::Inverse(ErrorResult& rv) { - if (GetMatrix().IsSingular()) { + gfxMatrix mat = GetMatrix(); + if (!mat.Invert()) { rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } - nsRefPtr<SVGMatrix> matrix = new SVGMatrix(gfxMatrix(GetMatrix()).Invert()); + nsRefPtr<SVGMatrix> matrix = new SVGMatrix(mat); return matrix.forget(); } already_AddRefed<SVGMatrix> SVGMatrix::Translate(float x, float y) { nsRefPtr<SVGMatrix> matrix = new SVGMatrix(gfxMatrix(GetMatrix()).Translate(gfxPoint(x, y)));
--- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -2547,36 +2547,42 @@ this.DOMApplicationRegistry = { // We notify about the successful installation via mgmt.oninstall and the // corresponding DOMRequest.onsuccess event as soon as the app is properly // saved in the registry. yield this._saveApps(); this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject }); + if (!aData.isPackage) { + this.updateAppHandlers(null, app.manifest, app); + if (aInstallSuccessCallback) { + try { + yield aInstallSuccessCallback(app, app.manifest); + } catch (e) { + // Ignore exceptions during the local installation of + // an app. If it fails, the app will anyway be considered + // as not installed because isLaunchable will return false. + } + } + } + // The presence of a requestID means that we have a page to update. if (aData.isPackage && aData.apkInstall && !aData.requestID) { // Skip directly to onInstallSuccessAck, since there isn't // a WebappsRegistry to receive Webapps:Install:Return:OK and respond // Webapps:Install:Return:Ack when an app is being auto-installed. this.onInstallSuccessAck(app.manifestURL); } else { // Broadcast Webapps:Install:Return:OK so the WebappsRegistry can notify // the installing page about the successful install, after which it'll // respond Webapps:Install:Return:Ack, which calls onInstallSuccessAck. this.broadcastMessage("Webapps:Install:Return:OK", aData); } - if (!aData.isPackage) { - this.updateAppHandlers(null, app.manifest, app); - if (aInstallSuccessCallback) { - aInstallSuccessCallback(app, app.manifest); - } - } - Services.obs.notifyObservers(null, "webapps-installed", JSON.stringify({ manifestURL: app.manifestURL })); if (aData.forceSuccessAck) { // If it's a local install, there's no content process so just // ack the install. this.onInstallSuccessAck(app.manifestURL, dontNeedNetwork); } @@ -2637,33 +2643,39 @@ this.DOMApplicationRegistry = { origin: aNewApp.origin, manifestURL: aNewApp.manifestURL }, true); } this.updateDataStore(this.webapps[aId].localId, aNewApp.origin, aNewApp.manifestURL, aManifest); + if (aInstallSuccessCallback) { + try { + yield aInstallSuccessCallback(aNewApp, aManifest, zipFile.path); + } catch (e) { + // Ignore exceptions during the local installation of + // an app. If it fails, the app will anyway be considered + // as not installed because isLaunchable will return false. + } + } + this.broadcastMessage("Webapps:UpdateState", { app: app, manifest: aManifest, manifestURL: aNewApp.manifestURL }); // Check if we have asm.js code to preload for this application. yield ScriptPreloader.preload(aNewApp, aManifest); this.broadcastMessage("Webapps:FireEvent", { eventType: ["downloadsuccess", "downloadapplied"], manifestURL: aNewApp.manifestURL }); - - if (aInstallSuccessCallback) { - aInstallSuccessCallback(aNewApp, aManifest, zipFile.path); - } }), _nextLocalId: function() { let id = Services.prefs.getIntPref("dom.mozApps.maxLocalId") + 1; while (this.getManifestURLByLocalId(id)) { id++; }
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -1237,17 +1237,19 @@ nsDOMWindowUtils::SendKeyEvent(const nsA event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD; break; } break; } event.refPoint.x = event.refPoint.y = 0; event.time = PR_IntervalNow(); - event.mFlags.mIsSynthesizedForTests = true; + if (!(aAdditionalFlags & KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS)) { + event.mFlags.mIsSynthesizedForTests = true; + } if (aAdditionalFlags & KEY_FLAG_PREVENT_DEFAULT) { event.mFlags.mDefaultPrevented = true; } nsEventStatus status; nsresult rv = widget->DispatchEvent(&event, status); NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -710,17 +710,16 @@ DumpString(const nsAString &str) #define JS_OPTIONS_DOT_STR "javascript.options." static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR; static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict"; #ifdef DEBUG static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug"; #endif -static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror"; #ifdef JS_GC_ZEAL static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal"; static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency"; #endif static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log"; static const char js_memnotify_option_str[] = JS_OPTIONS_DOT_STR "mem.notify"; void @@ -748,18 +747,16 @@ nsJSContext::JSOptionChangedCallback(con // In debug builds, warnings are enabled in chrome context if // javascript.options.strict.debug is true if (Preferences::GetBool(js_strict_debug_option_str) && (chromeWindow || !contentWindow)) { JS::ContextOptionsRef(cx).setExtraWarnings(true); } #endif - JS::ContextOptionsRef(cx).setWerror(Preferences::GetBool(js_werror_option_str)); - #ifdef JS_GC_ZEAL int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1); int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ); if (zeal >= 0) ::JS_SetGCZeal(context->mContext, (uint8_t)zeal, frequency); #endif }
--- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1898,21 +1898,20 @@ InterfaceHasInstance(JSContext* cx, JS:: "ID?"); if (domClass && domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) { *bp = true; return true; } - JS::Rooted<JSObject*> unwrapped(cx, js::CheckedUnwrap(instance, true)); - if (unwrapped && jsipc::IsCPOW(unwrapped)) { + if (jsipc::IsWrappedCPOW(instance)) { bool boolp = false; - if (!jsipc::DOMInstanceOf(cx, unwrapped, clasp->mPrototypeID, - clasp->mDepth, &boolp)) { + if (!jsipc::DOMInstanceOf(cx, js::CheckedUnwrap(instance), clasp->mPrototypeID, + clasp->mDepth, &boolp)) { return false; } *bp = boolp; return true; } JS::Rooted<JS::Value> protov(cx); DebugOnly<bool> ok = JS_GetProperty(cx, obj, "prototype", &protov);
--- a/dom/browser-element/BrowserElementPanning.js +++ b/dom/browser-element/BrowserElementPanning.js @@ -79,22 +79,40 @@ const ContentPanning = { // missing events if .stopPropagation() has been called. els.addSystemEventListener(global, type, this.handleEvent.bind(this), /* useCapture = */ false); }.bind(this)); }, handleEvent: function cp_handleEvent(evt) { - // Ignore events targeting a <iframe mozbrowser> since those will be - // handle by the BrowserElementPanning.js instance of it. + // Ignore events targeting an oop <iframe mozbrowser> since those will be + // handle by the BrowserElementPanning.js instance in the child process. if (evt.target instanceof Ci.nsIMozBrowserFrame) { return; } + // For in-process <iframe mozbrowser> the events are not targetting + // directly the container iframe element, but some node of the document. + // So, the BrowserElementPanning instance of the system app will receive + // the sequence of touch events, as well as the BrowserElementPanning + // instance in the targetted app. + // As a result, multiple mozbrowser iframes will try to interpret the + // sequence of touch events, which may results into multiple clicks. + let targetWindow = evt.target.ownerDocument.defaultView; + let frameElement = targetWindow.frameElement; + while (frameElement) { + targetWindow = frameElement.ownerDocument.defaultView; + frameElement = targetWindow.frameElement; + } + + if (content !== targetWindow) { + return; + } + if (evt.defaultPrevented || evt.multipleActionsPrevented) { // clean up panning state even if touchend/mouseup has been preventDefault. if(evt.type === 'touchend' || evt.type === 'mouseup') { if (this.dragging && (this.watchedEventsType === 'mouse' || this.findPrimaryPointer(evt.changedTouches))) { this._finishPanning(); }
--- a/dom/datastore/DataStoreDB.cpp +++ b/dom/datastore/DataStoreDB.cpp @@ -40,17 +40,17 @@ public: { nsString type; nsresult rv = aEvent->GetType(type); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!type.EqualsASCII("versionchange")) { - MOZ_ASSUME_UNREACHABLE("This should not happen"); + MOZ_ASSERT_UNREACHABLE("Expected a versionchange event"); return NS_ERROR_FAILURE; } rv = mDatabase->RemoveEventListener(NS_LITERAL_STRING("versionchange"), this, false); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
--- a/dom/inputmethod/forms.js +++ b/dom/inputmethod/forms.js @@ -503,27 +503,28 @@ let FormAssistant = { event.initEvent('input', true, false); target.dispatchEvent(event); break; } case "Forms:Input:SendKey": CompositionManager.endComposition(''); + let flags = domWindowUtils.KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS; this._editing = true; let doKeypress = domWindowUtils.sendKeyEvent('keydown', json.keyCode, - json.charCode, json.modifiers); + json.charCode, json.modifiers, flags); if (doKeypress) { domWindowUtils.sendKeyEvent('keypress', json.keyCode, - json.charCode, json.modifiers); + json.charCode, json.modifiers, flags); } if(!json.repeat) { domWindowUtils.sendKeyEvent('keyup', json.keyCode, - json.charCode, json.modifiers); + json.charCode, json.modifiers, flags); } this._editing = false; if (json.requestId && doKeypress) { sendAsyncMessage("Forms:SendKey:Result:OK", { requestId: json.requestId });
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -534,16 +534,20 @@ interface nsIDOMWindowUtils : nsISupport * @return false if the event had preventDefault() called on it, * true otherwise. In other words, true if and only if the * default action was taken. */ // If this is set, preventDefault() the event before dispatch. const unsigned long KEY_FLAG_PREVENT_DEFAULT = 0x0001; + // If this is set, the mIsSynthesizedForTests flag is set to false + // on the key event. Otherwise it is true. + const unsigned long KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS = 0x0002; + // if one of these flags is set, the KeyboardEvent.location will be the value. // Otherwise, it will be computed from aKeyCode. const unsigned long KEY_FLAG_LOCATION_STANDARD = 0x0010; const unsigned long KEY_FLAG_LOCATION_LEFT = 0x0020; const unsigned long KEY_FLAG_LOCATION_RIGHT = 0x0040; const unsigned long KEY_FLAG_LOCATION_NUMPAD = 0x0080; const unsigned long KEY_FLAG_LOCATION_MOBILE = 0x0100; const unsigned long KEY_FLAG_LOCATION_JOYSTICK = 0x0200;
--- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -6,16 +6,18 @@ "use strict"; const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PeerConnectionIdp", "resource://gre/modules/media/PeerConnectionIdp.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "convertToRTCStatsReport", + "resource://gre/modules/media/RTCStatsReport.jsm"); const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1"; const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1"; const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1"; const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1"; const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1"; const PC_STATS_CONTRACT = "@mozilla.org/dom/rtcstatsreport;1"; const PC_IDENTITY_CONTRACT = "@mozilla.org/dom/rtcidentityassertion;1"; @@ -182,26 +184,17 @@ function RTCStatsReport(win, dict) { function appendStats(stats, report) { stats.forEach(function(stat) { report[stat.id] = stat; }); } this._win = win; this._pcid = dict.pcid; - this._report = {}; - appendStats(dict.inboundRTPStreamStats, this._report); - appendStats(dict.outboundRTPStreamStats, this._report); - appendStats(dict.mediaStreamTrackStats, this._report); - appendStats(dict.mediaStreamStats, this._report); - appendStats(dict.transportStats, this._report); - appendStats(dict.iceComponentStats, this._report); - appendStats(dict.iceCandidatePairStats, this._report); - appendStats(dict.iceCandidateStats, this._report); - appendStats(dict.codecStats, this._report); + this._report = convertToRTCStatsReport(dict); } RTCStatsReport.prototype = { classDescription: "RTCStatsReport", classID: PC_STATS_CID, contractID: PC_STATS_CONTRACT, QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]), // TODO: Change to use webidl getters once available (Bug 952122)
new file mode 100644 --- /dev/null +++ b/dom/media/RTCStatsReport.jsm @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["convertToRTCStatsReport"]; + +function convertToRTCStatsReport(dict) { + function appendStats(stats, report) { + stats.forEach(function(stat) { + report[stat.id] = stat; + }); + } + let report = {}; + appendStats(dict.inboundRTPStreamStats, report); + appendStats(dict.outboundRTPStreamStats, report); + appendStats(dict.mediaStreamTrackStats, report); + appendStats(dict.mediaStreamStats, report); + appendStats(dict.transportStats, report); + appendStats(dict.iceComponentStats, report); + appendStats(dict.iceCandidatePairStats, report); + appendStats(dict.iceCandidateStats, report); + appendStats(dict.codecStats, report); + return report; +} + +this.convertToRTCStatsReport = convertToRTCStatsReport;
--- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -45,16 +45,17 @@ EXTRA_COMPONENTS += [ 'PeerConnection.manifest', ] JS_MODULES_PATH = 'modules/media' EXTRA_JS_MODULES += [ 'IdpProxy.jsm', 'PeerConnectionIdp.jsm', + 'RTCStatsReport.jsm', ] if CONFIG['MOZ_B2G']: EXPORTS.mozilla += [ 'MediaPermissionGonk.h', ] SOURCES += [ 'MediaPermissionGonk.cpp',
--- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -352,25 +352,25 @@ LoadRuntimeAndContextOptions(const char* runtimeOptions.setBaseline(true); } if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion"))) { runtimeOptions.setIon(true); } if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp"))) { runtimeOptions.setNativeRegExp(true); } + if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror"))) { + runtimeOptions.setWerror(true); + } // Common options. JS::ContextOptions commonContextOptions = kRequiredContextOptions; if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict"))) { commonContextOptions.setExtraWarnings(true); } - if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror"))) { - commonContextOptions.setWerror(true); - } // Content options. JS::ContextOptions contentContextOptions = commonContextOptions; // Chrome options. JS::ContextOptions chromeContextOptions = commonContextOptions; #ifdef DEBUG if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict.debug"))) {
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -3920,18 +3920,17 @@ WorkerPrivate::GetLoadInfo(JSContext* aC } } loadInfo.mXHRParamsAllowed = true; } MOZ_ASSERT(loadInfo.mPrincipal); MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty()); - if (!nsContentUtils::GetContentSecurityPolicy(aCx, - getter_AddRefs(loadInfo.mCSP))) { + if (!nsContentUtils::GetContentSecurityPolicy(getter_AddRefs(loadInfo.mCSP))) { NS_WARNING("Failed to get CSP!"); return NS_ERROR_FAILURE; } if (loadInfo.mCSP) { rv = loadInfo.mCSP->GetAllowsEval(&loadInfo.mReportCSPViolations, &loadInfo.mEvalAllowed); NS_ENSURE_SUCCESS(rv, rv);
--- a/gfx/2d/Matrix.h +++ b/gfx/2d/Matrix.h @@ -67,24 +67,34 @@ public: Matrix &Translate(Float aX, Float aY) { _31 += _11 * aX + _21 * aY; _32 += _12 * aX + _22 * aY; return *this; } + + Matrix &Translate(const Point &aPoint) + { + return Translate(aPoint.x, aPoint.y); + } Matrix &PostTranslate(Float aX, Float aY) { _31 += aX; _32 += aY; return *this; } + Matrix &PostTranslate(const Point &aPoint) + { + return PostTranslate(aPoint.x, aPoint.y); + } + Matrix &Rotate(Float aAngle) { return *this = Matrix::Rotation(aAngle) * *this; } bool Invert() { // Compute co-factors. @@ -189,16 +199,23 @@ public: */ bool HasNonIntegerTranslation() const { return HasNonTranslation() || !FuzzyEqual(_31, floor(_31 + 0.5)) || !FuzzyEqual(_32, floor(_32 + 0.5)); } /** + * Returns true if the matrix only has an integer translation. + */ + bool HasOnlyIntegerTranslation() const { + return !HasNonIntegerTranslation(); + } + + /** * Returns true if the matrix has any transform other * than a straight translation. */ bool HasNonTranslation() const { return !FuzzyEqual(_11, 1.0) || !FuzzyEqual(_22, 1.0) || !FuzzyEqual(_12, 0.0) || !FuzzyEqual(_21, 0.0); }
--- a/gfx/gl/GLTextureImage.cpp +++ b/gfx/gl/GLTextureImage.cpp @@ -4,17 +4,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GLTextureImage.h" #include "GLContext.h" #include "gfxContext.h" #include "gfxPlatform.h" #include "gfxUtils.h" #include "gfx2DGlue.h" -#include "gfxImageSurface.h" #include "mozilla/gfx/2D.h" #include "ScopedGLHelpers.h" #include "GLUploadHelpers.h" #include "TextureImageEGL.h" #ifdef XP_MACOSX #include "TextureImageCGL.h" #endif
--- a/gfx/layers/LayerScope.cpp +++ b/gfx/layers/LayerScope.cpp @@ -43,29 +43,27 @@ #include "nsIAsyncInputStream.h" #include "nsIEventTarget.h" #include "nsProxyRelease.h" // Undo the damage done by mozzconf.h #undef compress #include "mozilla/Compression.h" -#ifdef __GNUC__ -#define PACKED_STRUCT __attribute__((packed)) -#else -#define PACKED_STRUCT -#endif +// Protocol buffer (generated automatically) +#include "protobuf/LayerScopePacket.pb.h" namespace mozilla { namespace layers { using namespace mozilla::Compression; using namespace mozilla::gfx; using namespace mozilla::gl; using namespace mozilla; +using namespace layerscope; class DebugDataSender; class DebugGLData; /* This class handle websocket protocol which included * handshake and data frame's header */ class LayerScopeWebSocketHandler : public nsIInputStreamCallback { @@ -238,19 +236,19 @@ private: } // Client request is valid. Start to generate and send server response. nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); nsAutoCString res; SHA1Sum sha1; nsCString combined(wsKey + guid); sha1.update(combined.get(), combined.Length()); - uint8_t digest[SHA1Sum::HashSize]; // SHA1 digests are 20 bytes long. + uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long. sha1.finish(digest); - nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::HashSize); + nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize); Base64Encode(newString, res); nsCString response("HTTP/1.1 101 Switching Protocols\r\n"); response.AppendLiteral("Upgrade: websocket\r\n"); response.AppendLiteral("Connection: Upgrade\r\n"); response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n")); response.AppendLiteral("Sec-WebSocket-Protocol: binary\r\n\r\n"); uint32_t written = 0; @@ -346,243 +344,205 @@ public: // Destroy Web Server Socket if (sWebSocketManager) { sWebSocketManager->RemoveAllConnections(); } } static LayerScopeWebSocketManager* GetSocketManager() { - return sWebSocketManager.get(); + return sWebSocketManager; } private: static StaticAutoPtr<LayerScopeWebSocketManager> sWebSocketManager; }; StaticAutoPtr<LayerScopeWebSocketManager> WebSocketHelper::sWebSocketManager; -class DebugGLData : public LinkedListElement<DebugGLData> { +/* + * DebugGLData is the base class of + * 1. DebugGLFrameStatusData (Frame start/end packet) + * 2. DebugGLColorData (Color data packet) + * 3. DebugGLTextureData (Texture data packet) + */ +class DebugGLData: public LinkedListElement<DebugGLData> { public: - typedef enum { - FrameStart, - FrameEnd, - TextureData, - ColorData - } DataType; + DebugGLData(Packet::DataType aDataType) + : mDataType(aDataType) + { } virtual ~DebugGLData() { } - DataType GetDataType() const { return mDataType; } - intptr_t GetContextAddress() const { return mContextAddress; } - int64_t GetValue() const { return mValue; } - - DebugGLData(DataType dataType) - : mDataType(dataType), - mContextAddress(0), - mValue(0) - { } - - DebugGLData(DataType dataType, GLContext* cx) - : mDataType(dataType), - mContextAddress(reinterpret_cast<intptr_t>(cx)), - mValue(0) - { } + Packet::DataType GetDataType() const { return mDataType; } - DebugGLData(DataType dataType, GLContext* cx, int64_t value) - : mDataType(dataType), - mContextAddress(reinterpret_cast<intptr_t>(cx)), - mValue(value) - { } + virtual bool Write() = 0; - virtual bool Write() { - if (mDataType != FrameStart && - mDataType != FrameEnd) - { - NS_WARNING("Unimplemented data type!"); - return false; - } - - DebugGLData::BasicPacket packet; - - packet.type = mDataType; - packet.ptr = static_cast<uint64_t>(mContextAddress); - packet.value = mValue; - - return WriteToStream(&packet, sizeof(packet)); - } - - static bool WriteToStream(void *ptr, uint32_t size) { + static bool WriteToStream(Packet& aPacket) { if (!WebSocketHelper::GetSocketManager()) return true; - return WebSocketHelper::GetSocketManager()->WriteAll(ptr, size); + + uint32_t size = aPacket.ByteSize(); + nsAutoArrayPtr<uint8_t> data(new uint8_t[size]); + aPacket.SerializeToArray(data, size); + return WebSocketHelper::GetSocketManager()->WriteAll(data, size); } protected: - DataType mDataType; - intptr_t mContextAddress; - int64_t mValue; + Packet::DataType mDataType; +}; +class DebugGLFrameStatusData : public DebugGLData +{ public: - // the data packet formats; all packed -#ifdef _MSC_VER -#pragma pack(push, 1) -#endif - typedef struct { - uint32_t type; - uint64_t ptr; - uint64_t value; - } PACKED_STRUCT BasicPacket; + DebugGLFrameStatusData(Packet::DataType aDataType, + int64_t aValue) + : DebugGLData(aDataType), + mFrameStamp(aValue) + { } + + DebugGLFrameStatusData(Packet::DataType aDataType) + : DebugGLData(aDataType), + mFrameStamp(0) + { } - typedef struct { - uint32_t type; - uint64_t ptr; - uint64_t layerref; - uint32_t color; - uint32_t width; - uint32_t height; - } PACKED_STRUCT ColorPacket; + int64_t GetFrameStamp() const { return mFrameStamp; } + + virtual bool Write() MOZ_OVERRIDE { + Packet packet; + packet.set_type(mDataType); - typedef struct { - uint32_t type; - uint64_t ptr; - uint64_t layerref; - uint32_t name; - uint32_t width; - uint32_t height; - uint32_t stride; - uint32_t format; - uint32_t target; - uint32_t dataFormat; - uint32_t dataSize; - } PACKED_STRUCT TexturePacket; -#ifdef _MSC_VER -#pragma pack(pop) -#endif + FramePacket *fp = packet.mutable_frame(); + fp->set_value(mFrameStamp); + + if (!WriteToStream(packet)) + return false; + return true; + } + +protected: + int64_t mFrameStamp; }; class DebugGLTextureData : public DebugGLData { public: DebugGLTextureData(GLContext* cx, void* layerRef, GLenum target, GLuint name, DataSourceSurface* img) - : DebugGLData(DebugGLData::TextureData, cx), + : DebugGLData(Packet::TEXTURE), mLayerRef(layerRef), mTarget(target), mName(name), + mContextAddress(reinterpret_cast<intptr_t>(cx)), mDatasize(0) { // pre-packing // DataSourceSurface may have locked buffer, // so we should compress now, and then it could // be unlocked outside. pack(img); } - void *GetLayerRef() const { return mLayerRef; } + const void* GetLayerRef() const { return mLayerRef; } GLuint GetName() const { return mName; } GLenum GetTextureTarget() const { return mTarget; } + intptr_t GetContextAddress() const { return mContextAddress; } + uint32_t GetDataSize() const { return mDatasize; } - virtual bool Write() { - // write the packet header data - if (!WriteToStream(&mPacket, sizeof(mPacket))) - return false; - - // then the image data - if (mCompresseddata.get() && !WriteToStream(mCompresseddata, mDatasize)) + virtual bool Write() MOZ_OVERRIDE { + if (!WriteToStream(mPacket)) return false; - - // then pad out to 4 bytes - if (mDatasize % 4 != 0) { - static char buf[] = { 0, 0, 0, 0 }; - if (!WriteToStream(buf, 4 - (mDatasize % 4))) - return false; - } - return true; } private: void pack(DataSourceSurface* aImage) { - mPacket.type = mDataType; - mPacket.ptr = static_cast<uint64_t>(mContextAddress); - mPacket.layerref = reinterpret_cast<uint64_t>(mLayerRef); - mPacket.name = mName; - mPacket.format = 0; - mPacket.target = mTarget; - mPacket.dataFormat = LOCAL_GL_RGBA; + mPacket.set_type(mDataType); + + TexturePacket *tp = mPacket.mutable_texture(); + tp->set_layerref(reinterpret_cast<uint64_t>(mLayerRef)); + tp->set_name(mName); + tp->set_target(mTarget); + tp->set_dataformat(LOCAL_GL_RGBA); + tp->set_glcontext(static_cast<uint64_t>(mContextAddress)); if (aImage) { - mPacket.width = aImage->GetSize().width; - mPacket.height = aImage->GetSize().height; - mPacket.stride = aImage->Stride(); - mPacket.dataSize = aImage->GetSize().height * aImage->Stride(); + tp->set_width(aImage->GetSize().width); + tp->set_height(aImage->GetSize().height); + tp->set_stride(aImage->Stride()); + + mDatasize = aImage->GetSize().height * aImage->Stride(); - mCompresseddata = - new char[LZ4::maxCompressedSize(mPacket.dataSize)]; - if (mCompresseddata.get()) { + nsAutoArrayPtr<char> compresseddata( + new char[LZ4::maxCompressedSize(mDatasize)]); + if (compresseddata.get()) { int ndatasize = LZ4::compress((char*)aImage->GetData(), - mPacket.dataSize, - mCompresseddata); + mDatasize, + compresseddata); if (ndatasize > 0) { mDatasize = ndatasize; - - mPacket.dataFormat = (1 << 16) | mPacket.dataFormat; - mPacket.dataSize = mDatasize; + tp->set_dataformat((1 << 16 | tp->dataformat())); + tp->set_data(compresseddata, mDatasize); } else { NS_WARNING("Compress data failed"); + tp->set_data(aImage->GetData(), mDatasize); } } else { - NS_WARNING("Couldn't moz_malloc for compressed data."); + NS_WARNING("Couldn't new compressed data."); + tp->set_data(aImage->GetData(), mDatasize); } } else { - mPacket.width = 0; - mPacket.height = 0; - mPacket.stride = 0; - mPacket.dataSize = 0; + tp->set_width(0); + tp->set_height(0); + tp->set_stride(0); } } protected: void* mLayerRef; GLenum mTarget; GLuint mName; + intptr_t mContextAddress; + uint32_t mDatasize; // Packet data - DebugGLData::TexturePacket mPacket; - nsAutoArrayPtr<char> mCompresseddata; - uint32_t mDatasize; + Packet mPacket; }; class DebugGLColorData : public DebugGLData { public: - DebugGLColorData(void* layerRef, const gfxRGBA& color, int width, int height) - : DebugGLData(DebugGLData::ColorData), + DebugGLColorData(void* layerRef, + const gfxRGBA& color, + int width, + int height) + : DebugGLData(Packet::COLOR), mLayerRef(layerRef), mColor(color.Packed()), mSize(width, height) { } - void *GetLayerRef() const { return mLayerRef; } + const void* GetLayerRef() const { return mLayerRef; } uint32_t GetColor() const { return mColor; } const nsIntSize& GetSize() const { return mSize; } - virtual bool Write() { - DebugGLData::ColorPacket packet; + virtual bool Write() MOZ_OVERRIDE { + Packet packet; + packet.set_type(mDataType); - packet.type = mDataType; - packet.ptr = static_cast<uint64_t>(mContextAddress); - packet.layerref = reinterpret_cast<uintptr_t>(mLayerRef); - packet.color = mColor; - packet.width = mSize.width; - packet.height = mSize.height; + ColorPacket *cp = packet.mutable_color(); + cp->set_layerref(reinterpret_cast<uint64_t>(mLayerRef)); + cp->set_color(mColor); + cp->set_width(mSize.width); + cp->set_height(mSize.height); - return WriteToStream(&packet, sizeof(packet)); + if (!WriteToStream(packet)) + return false; + return true; } protected: void *mLayerRef; uint32_t mColor; nsIntSize mSize; }; @@ -1011,25 +971,25 @@ LayerScopeAutoFrame::~LayerScopeAutoFram void LayerScopeAutoFrame::BeginFrame(int64_t aFrameStamp) { if (!LayerScope::CheckSendable()) { return; } WebSocketHelper::GetSocketManager()->AppendDebugData( - new DebugGLData(DebugGLData::FrameStart, nullptr, aFrameStamp)); + new DebugGLFrameStatusData(Packet::FRAMESTART, aFrameStamp)); } void LayerScopeAutoFrame::EndFrame() { if (!LayerScope::CheckSendable()) { return; } WebSocketHelper::GetSocketManager()->AppendDebugData( - new DebugGLData(DebugGLData::FrameEnd, nullptr)); + new DebugGLFrameStatusData(Packet::FRAMEEND)); WebSocketHelper::GetSocketManager()->DispatchDebugData(); } } /* layers */ } /* mozilla */
--- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -208,16 +208,17 @@ public: virtual ClientLayerManager* AsClientLayerManager() { return nullptr; } /** * Returns true if this LayerManager is owned by an nsIWidget, * and is used for drawing into the widget. */ virtual bool IsWidgetLayerManager() { return true; } + virtual bool IsInactiveLayerManager() { return false; } /** * Start a new transaction. Nested transactions are not allowed so * there must be no transaction currently in progress. * This transaction will update the state of the window from which * this LayerManager was obtained. */ virtual void BeginTransaction() = 0;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -709,17 +709,16 @@ AsyncPanZoomController::AsyncPanZoomCont GeckoContentController* aGeckoContentController, GestureBehavior aGestures) : mLayersId(aLayersId), mPaintThrottler(GetFrameTime()), mGeckoContentController(aGeckoContentController), mRefPtrMonitor("RefPtrMonitor"), mSharingFrameMetricsAcrossProcesses(false), mMonitor("AsyncPanZoomController"), - mTouchActionPropertyEnabled(gfxPrefs::TouchActionEnabled()), mState(NOTHING), mContentResponseTimeoutTask(nullptr), mX(MOZ_THIS_IN_INITIALIZER_LIST()), mY(MOZ_THIS_IN_INITIALIZER_LIST()), mPanDirRestricted(false), mZoomConstraints(false, false, MIN_ZOOM, MAX_ZOOM), mLastSampleTime(GetFrameTime()), mLastAsyncScrollTime(GetFrameTime()), @@ -1006,17 +1005,17 @@ nsEventStatus AsyncPanZoomController::On case TOUCHING: { float panThreshold = GetTouchStartTolerance(); UpdateWithTouchAtDevicePoint(aEvent); if (PanDistance() < panThreshold) { return nsEventStatus_eIgnore; } - if (mTouchActionPropertyEnabled && + if (gfxPrefs::TouchActionEnabled() && (GetTouchBehavior(0) & AllowedTouchBehavior::VERTICAL_PAN) && (GetTouchBehavior(0) & AllowedTouchBehavior::HORIZONTAL_PAN)) { // User tries to trigger a touch behavior. If allowed touch behavior is vertical pan // + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault // status immediately to trigger cancel event further. It should happen independent of // the parent type (whether it is scrolling or not). StartPanning(aEvent); return nsEventStatus_eConsumeNoDefault; @@ -1574,17 +1573,17 @@ nsEventStatus AsyncPanZoomController::St // When the touch move breaks through the pan threshold, reposition the touch down origin // so the page won't jump when we start panning. mX.StartTouch(point.x, aEvent.mTime); mY.StartTouch(point.y, aEvent.mTime); double angle = atan2(dy, dx); // range [-pi, pi] angle = fabs(angle); // range [0, pi] - if (mTouchActionPropertyEnabled) { + if (gfxPrefs::TouchActionEnabled()) { HandlePanningWithTouchAction(angle, GetTouchBehavior(0)); } else { if (GetAxisLockMode() == FREE) { SetState(PANNING); } else { HandlePanning(angle); } } @@ -2483,17 +2482,17 @@ void AsyncPanZoomController::ContentRece void AsyncPanZoomController::CheckContentResponse() { bool canProceedToTouchState = true; if (mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) { canProceedToTouchState &= mTouchBlockState.mPreventDefaultSet; } - if (mTouchActionPropertyEnabled) { + if (gfxPrefs::TouchActionEnabled()) { canProceedToTouchState &= mTouchBlockState.mAllowedTouchBehaviorSet; } if (!canProceedToTouchState) { return; } if (mContentResponseTimeoutTask) { @@ -2522,31 +2521,31 @@ void AsyncPanZoomController::CheckConten mTouchQueue.RemoveElementAt(0); } mHandlingTouchQueue = false; } } bool AsyncPanZoomController::TouchActionAllowPinchZoom() { - if (!mTouchActionPropertyEnabled) { + if (!gfxPrefs::TouchActionEnabled()) { return true; } // Pointer events specification implies all touch points to allow zoom // to perform it. for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) { if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) { return false; } } return true; } bool AsyncPanZoomController::TouchActionAllowDoubleTapZoom() { - if (!mTouchActionPropertyEnabled) { + if (!gfxPrefs::TouchActionEnabled()) { return true; } for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) { if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) { return false; } } return true;
--- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -707,22 +707,16 @@ protected: // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the // monitor should be held. When setting |mState|, either the SetState() // function can be used, or the monitor can be held and then |mState| updated. // IMPORTANT: See the note about lock ordering at the top of APZCTreeManager.h. // This is mutable to allow entering it from 'const' methods; doing otherwise // would significantly limit what methods could be 'const'. mutable ReentrantMonitor mMonitor; - // Specifies whether we should use touch-action css property. Initialized from - // the preferences. This property (in comparison with the global one) simplifies - // testing apzc with (and without) touch-action property enabled concurrently - // (e.g. with the gtest framework). - bool mTouchActionPropertyEnabled; - // Stores the state of panning and zooming this frame. This is protected by // |mMonitor|; that is, it should be held whenever this is updated. PanZoomState mState; private: // Metrics of the container layer corresponding to this APZC. This is // stored here so that it is accessible from the UI/controller thread. // These are the metrics at last content paint, the most recent
--- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -205,34 +205,39 @@ public: gfxContextMatrixAutoSaveRestore mTargetMatrixSR; Layer* mLayer; LayerManager::DrawThebesLayerCallback mCallback; void* mCallbackData; Matrix mTransform; bool mPushedOpaqueRect; }; -BasicLayerManager::BasicLayerManager(nsIWidget* aWidget) : - mPhase(PHASE_NONE), - mWidget(aWidget) - , mDoubleBuffering(BufferMode::BUFFER_NONE), mUsingDefaultTarget(false) +BasicLayerManager::BasicLayerManager(nsIWidget* aWidget) + : mPhase(PHASE_NONE) + , mWidget(aWidget) + , mDoubleBuffering(BufferMode::BUFFER_NONE) + , mType(BLM_WIDGET) + , mUsingDefaultTarget(false) , mTransactionIncomplete(false) , mCompositorMightResample(false) { MOZ_COUNT_CTOR(BasicLayerManager); NS_ASSERTION(aWidget, "Must provide a widget"); } -BasicLayerManager::BasicLayerManager() : - mPhase(PHASE_NONE), - mWidget(nullptr) - , mDoubleBuffering(BufferMode::BUFFER_NONE), mUsingDefaultTarget(false) +BasicLayerManager::BasicLayerManager(BasicLayerManagerType aType) + : mPhase(PHASE_NONE) + , mWidget(nullptr) + , mDoubleBuffering(BufferMode::BUFFER_NONE) + , mType(aType) + , mUsingDefaultTarget(false) , mTransactionIncomplete(false) { MOZ_COUNT_CTOR(BasicLayerManager); + MOZ_ASSERT(mType != BLM_WIDGET); } BasicLayerManager::~BasicLayerManager() { NS_ASSERTION(!InTransaction(), "Died during transaction?"); ClearCachedResources();
--- a/gfx/layers/basic/BasicLayers.h +++ b/gfx/layers/basic/BasicLayers.h @@ -40,23 +40,28 @@ class ReadbackProcessor; * the drawing phase, each ThebesLayer is painted directly into the target * context (with appropriate clipping and Push/PopGroups performed * between layers). */ class BasicLayerManager : public LayerManager { public: + enum BasicLayerManagerType { + BLM_WIDGET, + BLM_OFFSCREEN, + BLM_INACTIVE + }; /** * Construct a BasicLayerManager which will have no default * target context. SetDefaultTarget or BeginTransactionWithTarget * must be called for any rendering to happen. ThebesLayers will not * be retained. */ - BasicLayerManager(); + BasicLayerManager(BasicLayerManagerType aType); /** * Construct a BasicLayerManager which will have no default * target context. SetDefaultTarget or BeginTransactionWithTarget * must be called for any rendering to happen. ThebesLayers will be * retained; that is, we will try to retain the visible contents of * ThebesLayers as cairo surfaces. We create ThebesLayer buffers by * creating similar surfaces to the default target context, or to * aWidget's GetThebesSurface if there is no default target context, or @@ -84,16 +89,17 @@ public: void SetDefaultTarget(gfxContext* aContext); virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation); gfxContext* GetDefaultTarget() { return mDefaultTarget; } nsIWidget* GetRetainerWidget() { return mWidget; } void ClearRetainerWidget() { mWidget = nullptr; } virtual bool IsWidgetLayerManager() { return mWidget != nullptr; } + virtual bool IsInactiveLayerManager() { return mType == BLM_INACTIVE; } virtual void BeginTransaction(); virtual void BeginTransactionWithTarget(gfxContext* aTarget); virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT); virtual void EndTransaction(DrawThebesLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags = END_DEFAULT); virtual bool AreComponentAlphaLayersEnabled() { return !IsWidgetLayerManager(); } @@ -178,16 +184,17 @@ protected: // The default context for BeginTransaction. nsRefPtr<gfxContext> mDefaultTarget; // The context to draw into. nsRefPtr<gfxContext> mTarget; // Image factory we use. nsRefPtr<ImageFactory> mFactory; BufferMode mDoubleBuffering; + BasicLayerManagerType mType; bool mUsingDefaultTarget; bool mTransactionIncomplete; bool mCompositorMightResample; }; } }
--- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -300,29 +300,33 @@ UNIFIED_SOURCES += [ 'LayersLogging.cpp', 'LayerSorter.cpp', 'opengl/CompositingRenderTargetOGL.cpp', 'opengl/CompositorOGL.cpp', 'opengl/OGLShaderProgram.cpp', 'opengl/TextureClientOGL.cpp', 'opengl/TextureHostOGL.cpp', 'opengl/TexturePoolOGL.cpp', + 'protobuf/LayerScopePacket.pb.cc', 'ReadbackProcessor.cpp', 'RenderTrace.cpp', 'RotatedBuffer.cpp', 'YCbCrImageDataSerializer.cpp', ] SOURCES += [ 'basic/BasicImageLayer.cpp', 'ImageContainer.cpp', 'Layers.cpp', 'LayerTreeInvalidation.cpp', ] +# Disable RTTI in google protocol buffer +DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True + # Workaround compiler bug (Bug 795594) if CONFIG['_MSC_VER'] and CONFIG['CPU_ARCH'] == 'x86_64': for src in [ 'Layers.cpp', 'LayerTreeInvalidation.cpp', ]: SOURCES[src].no_pgo = True
new file mode 100644 --- /dev/null +++ b/gfx/layers/protobuf/LayerScopePacket.pb.cc @@ -0,0 +1,1231 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "LayerScopePacket.pb.h" + +#include <algorithm> + +#include <google/protobuf/stubs/once.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/wire_format_lite_inl.h> +// @@protoc_insertion_point(includes) + +namespace mozilla { +namespace layers { +namespace layerscope { + +void protobuf_ShutdownFile_LayerScopePacket_2eproto() { + delete FramePacket::default_instance_; + delete ColorPacket::default_instance_; + delete TexturePacket::default_instance_; + delete Packet::default_instance_; +} + +void protobuf_AddDesc_LayerScopePacket_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + FramePacket::default_instance_ = new FramePacket(); + ColorPacket::default_instance_ = new ColorPacket(); + TexturePacket::default_instance_ = new TexturePacket(); + Packet::default_instance_ = new Packet(); + FramePacket::default_instance_->InitAsDefaultInstance(); + ColorPacket::default_instance_->InitAsDefaultInstance(); + TexturePacket::default_instance_->InitAsDefaultInstance(); + Packet::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_LayerScopePacket_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_LayerScopePacket_2eproto { + StaticDescriptorInitializer_LayerScopePacket_2eproto() { + protobuf_AddDesc_LayerScopePacket_2eproto(); + } +} static_descriptor_initializer_LayerScopePacket_2eproto_; + + +// =================================================================== + +#ifndef _MSC_VER +const int FramePacket::kValueFieldNumber; +#endif // !_MSC_VER + +FramePacket::FramePacket() + : ::google::protobuf::MessageLite() { + SharedCtor(); +} + +void FramePacket::InitAsDefaultInstance() { +} + +FramePacket::FramePacket(const FramePacket& from) + : ::google::protobuf::MessageLite() { + SharedCtor(); + MergeFrom(from); +} + +void FramePacket::SharedCtor() { + _cached_size_ = 0; + value_ = GOOGLE_ULONGLONG(0); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +FramePacket::~FramePacket() { + SharedDtor(); +} + +void FramePacket::SharedDtor() { + if (this != default_instance_) { + } +} + +void FramePacket::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const FramePacket& FramePacket::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto(); return *default_instance_; +} + +FramePacket* FramePacket::default_instance_ = NULL; + +FramePacket* FramePacket::New() const { + return new FramePacket; +} + +void FramePacket::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + value_ = GOOGLE_ULONGLONG(0); + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +bool FramePacket::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional uint64 value = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &value_))); + set_has_value(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } + return true; +#undef DO_ +} + +void FramePacket::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional uint64 value = 1; + if (has_value()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->value(), output); + } + +} + +int FramePacket::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional uint64 value = 1; + if (has_value()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->value()); + } + + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void FramePacket::CheckTypeAndMergeFrom( + const ::google::protobuf::MessageLite& from) { + MergeFrom(*::google::protobuf::down_cast<const FramePacket*>(&from)); +} + +void FramePacket::MergeFrom(const FramePacket& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_value()) { + set_value(from.value()); + } + } +} + +void FramePacket::CopyFrom(const FramePacket& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FramePacket::IsInitialized() const { + + return true; +} + +void FramePacket::Swap(FramePacket* other) { + if (other != this) { + std::swap(value_, other->value_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::std::string FramePacket::GetTypeName() const { + return "mozilla.layers.layerscope.FramePacket"; +} + + +// =================================================================== + +#ifndef _MSC_VER +const int ColorPacket::kLayerrefFieldNumber; +const int ColorPacket::kWidthFieldNumber; +const int ColorPacket::kHeightFieldNumber; +const int ColorPacket::kColorFieldNumber; +#endif // !_MSC_VER + +ColorPacket::ColorPacket() + : ::google::protobuf::MessageLite() { + SharedCtor(); +} + +void ColorPacket::InitAsDefaultInstance() { +} + +ColorPacket::ColorPacket(const ColorPacket& from) + : ::google::protobuf::MessageLite() { + SharedCtor(); + MergeFrom(from); +} + +void ColorPacket::SharedCtor() { + _cached_size_ = 0; + layerref_ = GOOGLE_ULONGLONG(0); + width_ = 0u; + height_ = 0u; + color_ = 0u; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +ColorPacket::~ColorPacket() { + SharedDtor(); +} + +void ColorPacket::SharedDtor() { + if (this != default_instance_) { + } +} + +void ColorPacket::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ColorPacket& ColorPacket::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto(); return *default_instance_; +} + +ColorPacket* ColorPacket::default_instance_ = NULL; + +ColorPacket* ColorPacket::New() const { + return new ColorPacket; +} + +void ColorPacket::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + layerref_ = GOOGLE_ULONGLONG(0); + width_ = 0u; + height_ = 0u; + color_ = 0u; + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +bool ColorPacket::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // required uint64 layerref = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &layerref_))); + set_has_layerref(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(16)) goto parse_width; + break; + } + + // optional uint32 width = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_width: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &width_))); + set_has_width(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(24)) goto parse_height; + break; + } + + // optional uint32 height = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_height: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &height_))); + set_has_height(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(32)) goto parse_color; + break; + } + + // optional uint32 color = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_color: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &color_))); + set_has_color(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } + return true; +#undef DO_ +} + +void ColorPacket::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // required uint64 layerref = 1; + if (has_layerref()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->layerref(), output); + } + + // optional uint32 width = 2; + if (has_width()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(2, this->width(), output); + } + + // optional uint32 height = 3; + if (has_height()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(3, this->height(), output); + } + + // optional uint32 color = 4; + if (has_color()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->color(), output); + } + +} + +int ColorPacket::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // required uint64 layerref = 1; + if (has_layerref()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->layerref()); + } + + // optional uint32 width = 2; + if (has_width()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->width()); + } + + // optional uint32 height = 3; + if (has_height()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->height()); + } + + // optional uint32 color = 4; + if (has_color()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->color()); + } + + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void ColorPacket::CheckTypeAndMergeFrom( + const ::google::protobuf::MessageLite& from) { + MergeFrom(*::google::protobuf::down_cast<const ColorPacket*>(&from)); +} + +void ColorPacket::MergeFrom(const ColorPacket& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_layerref()) { + set_layerref(from.layerref()); + } + if (from.has_width()) { + set_width(from.width()); + } + if (from.has_height()) { + set_height(from.height()); + } + if (from.has_color()) { + set_color(from.color()); + } + } +} + +void ColorPacket::CopyFrom(const ColorPacket& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ColorPacket::IsInitialized() const { + if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false; + + return true; +} + +void ColorPacket::Swap(ColorPacket* other) { + if (other != this) { + std::swap(layerref_, other->layerref_); + std::swap(width_, other->width_); + std::swap(height_, other->height_); + std::swap(color_, other->color_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::std::string ColorPacket::GetTypeName() const { + return "mozilla.layers.layerscope.ColorPacket"; +} + + +// =================================================================== + +#ifndef _MSC_VER +const int TexturePacket::kLayerrefFieldNumber; +const int TexturePacket::kWidthFieldNumber; +const int TexturePacket::kHeightFieldNumber; +const int TexturePacket::kStrideFieldNumber; +const int TexturePacket::kNameFieldNumber; +const int TexturePacket::kTargetFieldNumber; +const int TexturePacket::kDataformatFieldNumber; +const int TexturePacket::kGlcontextFieldNumber; +const int TexturePacket::kDataFieldNumber; +#endif // !_MSC_VER + +TexturePacket::TexturePacket() + : ::google::protobuf::MessageLite() { + SharedCtor(); +} + +void TexturePacket::InitAsDefaultInstance() { +} + +TexturePacket::TexturePacket(const TexturePacket& from) + : ::google::protobuf::MessageLite() { + SharedCtor(); + MergeFrom(from); +} + +void TexturePacket::SharedCtor() { + _cached_size_ = 0; + layerref_ = GOOGLE_ULONGLONG(0); + width_ = 0u; + height_ = 0u; + stride_ = 0u; + name_ = 0u; + target_ = 0u; + dataformat_ = 0u; + glcontext_ = GOOGLE_ULONGLONG(0); + data_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +TexturePacket::~TexturePacket() { + SharedDtor(); +} + +void TexturePacket::SharedDtor() { + if (data_ != &::google::protobuf::internal::kEmptyString) { + delete data_; + } + if (this != default_instance_) { + } +} + +void TexturePacket::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const TexturePacket& TexturePacket::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto(); return *default_instance_; +} + +TexturePacket* TexturePacket::default_instance_ = NULL; + +TexturePacket* TexturePacket::New() const { + return new TexturePacket; +} + +void TexturePacket::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + layerref_ = GOOGLE_ULONGLONG(0); + width_ = 0u; + height_ = 0u; + stride_ = 0u; + name_ = 0u; + target_ = 0u; + dataformat_ = 0u; + glcontext_ = GOOGLE_ULONGLONG(0); + } + if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) { + if (has_data()) { + if (data_ != &::google::protobuf::internal::kEmptyString) { + data_->clear(); + } + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +bool TexturePacket::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // required uint64 layerref = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &layerref_))); + set_has_layerref(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(16)) goto parse_width; + break; + } + + // optional uint32 width = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_width: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &width_))); + set_has_width(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(24)) goto parse_height; + break; + } + + // optional uint32 height = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_height: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &height_))); + set_has_height(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(32)) goto parse_stride; + break; + } + + // optional uint32 stride = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_stride: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &stride_))); + set_has_stride(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(40)) goto parse_name; + break; + } + + // optional uint32 name = 5; + case 5: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_name: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &name_))); + set_has_name(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(48)) goto parse_target; + break; + } + + // optional uint32 target = 6; + case 6: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_target: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &target_))); + set_has_target(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(56)) goto parse_dataformat; + break; + } + + // optional uint32 dataformat = 7; + case 7: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_dataformat: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &dataformat_))); + set_has_dataformat(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(64)) goto parse_glcontext; + break; + } + + // optional uint64 glcontext = 8; + case 8: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_glcontext: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &glcontext_))); + set_has_glcontext(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(74)) goto parse_data; + break; + } + + // optional bytes data = 9; + case 9: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_data: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_data())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } + return true; +#undef DO_ +} + +void TexturePacket::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // required uint64 layerref = 1; + if (has_layerref()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->layerref(), output); + } + + // optional uint32 width = 2; + if (has_width()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(2, this->width(), output); + } + + // optional uint32 height = 3; + if (has_height()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(3, this->height(), output); + } + + // optional uint32 stride = 4; + if (has_stride()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->stride(), output); + } + + // optional uint32 name = 5; + if (has_name()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(5, this->name(), output); + } + + // optional uint32 target = 6; + if (has_target()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(6, this->target(), output); + } + + // optional uint32 dataformat = 7; + if (has_dataformat()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(7, this->dataformat(), output); + } + + // optional uint64 glcontext = 8; + if (has_glcontext()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(8, this->glcontext(), output); + } + + // optional bytes data = 9; + if (has_data()) { + ::google::protobuf::internal::WireFormatLite::WriteBytes( + 9, this->data(), output); + } + +} + +int TexturePacket::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // required uint64 layerref = 1; + if (has_layerref()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->layerref()); + } + + // optional uint32 width = 2; + if (has_width()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->width()); + } + + // optional uint32 height = 3; + if (has_height()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->height()); + } + + // optional uint32 stride = 4; + if (has_stride()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->stride()); + } + + // optional uint32 name = 5; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->name()); + } + + // optional uint32 target = 6; + if (has_target()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->target()); + } + + // optional uint32 dataformat = 7; + if (has_dataformat()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->dataformat()); + } + + // optional uint64 glcontext = 8; + if (has_glcontext()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->glcontext()); + } + + } + if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) { + // optional bytes data = 9; + if (has_data()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->data()); + } + + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void TexturePacket::CheckTypeAndMergeFrom( + const ::google::protobuf::MessageLite& from) { + MergeFrom(*::google::protobuf::down_cast<const TexturePacket*>(&from)); +} + +void TexturePacket::MergeFrom(const TexturePacket& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_layerref()) { + set_layerref(from.layerref()); + } + if (from.has_width()) { + set_width(from.width()); + } + if (from.has_height()) { + set_height(from.height()); + } + if (from.has_stride()) { + set_stride(from.stride()); + } + if (from.has_name()) { + set_name(from.name()); + } + if (from.has_target()) { + set_target(from.target()); + } + if (from.has_dataformat()) { + set_dataformat(from.dataformat()); + } + if (from.has_glcontext()) { + set_glcontext(from.glcontext()); + } + } + if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) { + if (from.has_data()) { + set_data(from.data()); + } + } +} + +void TexturePacket::CopyFrom(const TexturePacket& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool TexturePacket::IsInitialized() const { + if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false; + + return true; +} + +void TexturePacket::Swap(TexturePacket* other) { + if (other != this) { + std::swap(layerref_, other->layerref_); + std::swap(width_, other->width_); + std::swap(height_, other->height_); + std::swap(stride_, other->stride_); + std::swap(name_, other->name_); + std::swap(target_, other->target_); + std::swap(dataformat_, other->dataformat_); + std::swap(glcontext_, other->glcontext_); + std::swap(data_, other->data_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::std::string TexturePacket::GetTypeName() const { + return "mozilla.layers.layerscope.TexturePacket"; +} + + +// =================================================================== + +bool Packet_DataType_IsValid(int value) { + switch(value) { + case 1: + case 2: + case 3: + case 4: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const Packet_DataType Packet::FRAMESTART; +const Packet_DataType Packet::FRAMEEND; +const Packet_DataType Packet::COLOR; +const Packet_DataType Packet::TEXTURE; +const Packet_DataType Packet::DataType_MIN; +const Packet_DataType Packet::DataType_MAX; +const int Packet::DataType_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +const int Packet::kTypeFieldNumber; +const int Packet::kFrameFieldNumber; +const int Packet::kColorFieldNumber; +const int Packet::kTextureFieldNumber; +#endif // !_MSC_VER + +Packet::Packet() + : ::google::protobuf::MessageLite() { + SharedCtor(); +} + +void Packet::InitAsDefaultInstance() { + frame_ = const_cast< ::mozilla::layers::layerscope::FramePacket*>(&::mozilla::layers::layerscope::FramePacket::default_instance()); + color_ = const_cast< ::mozilla::layers::layerscope::ColorPacket*>(&::mozilla::layers::layerscope::ColorPacket::default_instance()); + texture_ = const_cast< ::mozilla::layers::layerscope::TexturePacket*>(&::mozilla::layers::layerscope::TexturePacket::default_instance()); +} + +Packet::Packet(const Packet& from) + : ::google::protobuf::MessageLite() { + SharedCtor(); + MergeFrom(from); +} + +void Packet::SharedCtor() { + _cached_size_ = 0; + type_ = 1; + frame_ = NULL; + color_ = NULL; + texture_ = NULL; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Packet::~Packet() { + SharedDtor(); +} + +void Packet::SharedDtor() { + if (this != default_instance_) { + delete frame_; + delete color_; + delete texture_; + } +} + +void Packet::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const Packet& Packet::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_LayerScopePacket_2eproto(); return *default_instance_; +} + +Packet* Packet::default_instance_ = NULL; + +Packet* Packet::New() const { + return new Packet; +} + +void Packet::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + type_ = 1; + if (has_frame()) { + if (frame_ != NULL) frame_->::mozilla::layers::layerscope::FramePacket::Clear(); + } + if (has_color()) { + if (color_ != NULL) color_->::mozilla::layers::layerscope::ColorPacket::Clear(); + } + if (has_texture()) { + if (texture_ != NULL) texture_->::mozilla::layers::layerscope::TexturePacket::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +bool Packet::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // required .mozilla.layers.layerscope.Packet.DataType type = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::mozilla::layers::layerscope::Packet_DataType_IsValid(value)) { + set_type(static_cast< ::mozilla::layers::layerscope::Packet_DataType >(value)); + } + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_frame; + break; + } + + // optional .mozilla.layers.layerscope.FramePacket frame = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_frame: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_frame())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_color; + break; + } + + // optional .mozilla.layers.layerscope.ColorPacket color = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_color: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_color())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(34)) goto parse_texture; + break; + } + + // optional .mozilla.layers.layerscope.TexturePacket texture = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_texture: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_texture())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag)); + break; + } + } + } + return true; +#undef DO_ +} + +void Packet::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // required .mozilla.layers.layerscope.Packet.DataType type = 1; + if (has_type()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 1, this->type(), output); + } + + // optional .mozilla.layers.layerscope.FramePacket frame = 2; + if (has_frame()) { + ::google::protobuf::internal::WireFormatLite::WriteMessage( + 2, this->frame(), output); + } + + // optional .mozilla.layers.layerscope.ColorPacket color = 3; + if (has_color()) { + ::google::protobuf::internal::WireFormatLite::WriteMessage( + 3, this->color(), output); + } + + // optional .mozilla.layers.layerscope.TexturePacket texture = 4; + if (has_texture()) { + ::google::protobuf::internal::WireFormatLite::WriteMessage( + 4, this->texture(), output); + } + +} + +int Packet::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // required .mozilla.layers.layerscope.Packet.DataType type = 1; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->type()); + } + + // optional .mozilla.layers.layerscope.FramePacket frame = 2; + if (has_frame()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->frame()); + } + + // optional .mozilla.layers.layerscope.ColorPacket color = 3; + if (has_color()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->color()); + } + + // optional .mozilla.layers.layerscope.TexturePacket texture = 4; + if (has_texture()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->texture()); + } + + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Packet::CheckTypeAndMergeFrom( + const ::google::protobuf::MessageLite& from) { + MergeFrom(*::google::protobuf::down_cast<const Packet*>(&from)); +} + +void Packet::MergeFrom(const Packet& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_type()) { + set_type(from.type()); + } + if (from.has_frame()) { + mutable_frame()->::mozilla::layers::layerscope::FramePacket::MergeFrom(from.frame()); + } + if (from.has_color()) { + mutable_color()->::mozilla::layers::layerscope::ColorPacket::MergeFrom(from.color()); + } + if (from.has_texture()) { + mutable_texture()->::mozilla::layers::layerscope::TexturePacket::MergeFrom(from.texture()); + } + } +} + +void Packet::CopyFrom(const Packet& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Packet::IsInitialized() const { + if ((_has_bits_[0] & 0x00000001) != 0x00000001) return false; + + if (has_color()) { + if (!this->color().IsInitialized()) return false; + } + if (has_texture()) { + if (!this->texture().IsInitialized()) return false; + } + return true; +} + +void Packet::Swap(Packet* other) { + if (other != this) { + std::swap(type_, other->type_); + std::swap(frame_, other->frame_); + std::swap(color_, other->color_); + std::swap(texture_, other->texture_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::std::string Packet::GetTypeName() const { + return "mozilla.layers.layerscope.Packet"; +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace layerscope +} // namespace layers +} // namespace mozilla + +// @@protoc_insertion_point(global_scope)
new file mode 100644 --- /dev/null +++ b/gfx/layers/protobuf/LayerScopePacket.pb.h @@ -0,0 +1,972 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: LayerScopePacket.proto + +#ifndef PROTOBUF_LayerScopePacket_2eproto__INCLUDED +#define PROTOBUF_LayerScopePacket_2eproto__INCLUDED + +#include <string> + +#include <google/protobuf/stubs/common.h> + +#if GOOGLE_PROTOBUF_VERSION < 2004000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2004001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include <google/protobuf/generated_message_util.h> +#include <google/protobuf/repeated_field.h> +#include <google/protobuf/extension_set.h> +// @@protoc_insertion_point(includes) + +namespace mozilla { +namespace layers { +namespace layerscope { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_LayerScopePacket_2eproto(); +void protobuf_AssignDesc_LayerScopePacket_2eproto(); +void protobuf_ShutdownFile_LayerScopePacket_2eproto(); + +class FramePacket; +class ColorPacket; +class TexturePacket; +class Packet; + +enum Packet_DataType { + Packet_DataType_FRAMESTART = 1, + Packet_DataType_FRAMEEND = 2, + Packet_DataType_COLOR = 3, + Packet_DataType_TEXTURE = 4 +}; +bool Packet_DataType_IsValid(int value); +const Packet_DataType Packet_DataType_DataType_MIN = Packet_DataType_FRAMESTART; +const Packet_DataType Packet_DataType_DataType_MAX = Packet_DataType_TEXTURE; +const int Packet_DataType_DataType_ARRAYSIZE = Packet_DataType_DataType_MAX + 1; + +// =================================================================== + +class FramePacket : public ::google::protobuf::MessageLite { + public: + FramePacket(); + virtual ~FramePacket(); + + FramePacket(const FramePacket& from); + + inline FramePacket& operator=(const FramePacket& from) { + CopyFrom(from); + return *this; + } + + static const FramePacket& default_instance(); + + void Swap(FramePacket* other); + + // implements Message ---------------------------------------------- + + FramePacket* New() const; + void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from); + void CopyFrom(const FramePacket& from); + void MergeFrom(const FramePacket& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::std::string GetTypeName() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional uint64 value = 1; + inline bool has_value() const; + inline void clear_value(); + static const int kValueFieldNumber = 1; + inline ::google::protobuf::uint64 value() const; + inline void set_value(::google::protobuf::uint64 value); + + // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.FramePacket) + private: + inline void set_has_value(); + inline void clear_has_value(); + + ::google::protobuf::uint64 value_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; + + friend void protobuf_AddDesc_LayerScopePacket_2eproto(); + friend void protobuf_AssignDesc_LayerScopePacket_2eproto(); + friend void protobuf_ShutdownFile_LayerScopePacket_2eproto(); + + void InitAsDefaultInstance(); + static FramePacket* default_instance_; +}; +// ------------------------------------------------------------------- + +class ColorPacket : public ::google::protobuf::MessageLite { + public: + ColorPacket(); + virtual ~ColorPacket(); + + ColorPacket(const ColorPacket& from); + + inline ColorPacket& operator=(const ColorPacket& from) { + CopyFrom(from); + return *this; + } + + static const ColorPacket& default_instance(); + + void Swap(ColorPacket* other); + + // implements Message ---------------------------------------------- + + ColorPacket* New() const; + void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from); + void CopyFrom(const ColorPacket& from); + void MergeFrom(const ColorPacket& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::std::string GetTypeName() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // required uint64 layerref = 1; + inline bool has_layerref() const; + inline void clear_layerref(); + static const int kLayerrefFieldNumber = 1; + inline ::google::protobuf::uint64 layerref() const; + inline void set_layerref(::google::protobuf::uint64 value); + + // optional uint32 width = 2; + inline bool has_width() const; + inline void clear_width(); + static const int kWidthFieldNumber = 2; + inline ::google::protobuf::uint32 width() const; + inline void set_width(::google::protobuf::uint32 value); + + // optional uint32 height = 3; + inline bool has_height() const; + inline void clear_height(); + static const int kHeightFieldNumber = 3; + inline ::google::protobuf::uint32 height() const; + inline void set_height(::google::protobuf::uint32 value); + + // optional uint32 color = 4; + inline bool has_color() const; + inline void clear_color(); + static const int kColorFieldNumber = 4; + inline ::google::protobuf::uint32 color() const; + inline void set_color(::google::protobuf::uint32 value); + + // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.ColorPacket) + private: + inline void set_has_layerref(); + inline void clear_has_layerref(); + inline void set_has_width(); + inline void clear_has_width(); + inline void set_has_height(); + inline void clear_has_height(); + inline void set_has_color(); + inline void clear_has_color(); + + ::google::protobuf::uint64 layerref_; + ::google::protobuf::uint32 width_; + ::google::protobuf::uint32 height_; + ::google::protobuf::uint32 color_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + friend void protobuf_AddDesc_LayerScopePacket_2eproto(); + friend void protobuf_AssignDesc_LayerScopePacket_2eproto(); + friend void protobuf_ShutdownFile_LayerScopePacket_2eproto(); + + void InitAsDefaultInstance(); + static ColorPacket* default_instance_; +}; +// ------------------------------------------------------------------- + +class TexturePacket : public ::google::protobuf::MessageLite { + public: + TexturePacket(); + virtual ~TexturePacket(); + + TexturePacket(const TexturePacket& from); + + inline TexturePacket& operator=(const TexturePacket& from) { + CopyFrom(from); + return *this; + } + + static const TexturePacket& default_instance(); + + void Swap(TexturePacket* other); + + // implements Message ---------------------------------------------- + + TexturePacket* New() const; + void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from); + void CopyFrom(const TexturePacket& from); + void MergeFrom(const TexturePacket& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::std::string GetTypeName() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // required uint64 layerref = 1; + inline bool has_layerref() const; + inline void clear_layerref(); + static const int kLayerrefFieldNumber = 1; + inline ::google::protobuf::uint64 layerref() const; + inline void set_layerref(::google::protobuf::uint64 value); + + // optional uint32 width = 2; + inline bool has_width() const; + inline void clear_width(); + static const int kWidthFieldNumber = 2; + inline ::google::protobuf::uint32 width() const; + inline void set_width(::google::protobuf::uint32 value); + + // optional uint32 height = 3; + inline bool has_height() const; + inline void clear_height(); + static const int kHeightFieldNumber = 3; + inline ::google::protobuf::uint32 height() const; + inline void set_height(::google::protobuf::uint32 value); + + // optional uint32 stride = 4; + inline bool has_stride() const; + inline void clear_stride(); + static const int kStrideFieldNumber = 4; + inline ::google::protobuf::uint32 stride() const; + inline void set_stride(::google::protobuf::uint32 value); + + // optional uint32 name = 5; + inline bool has_name() const; + inline void clear_name(); + static const int kNameFieldNumber = 5; + inline ::google::protobuf::uint32 name() const; + inline void set_name(::google::protobuf::uint32 value); + + // optional uint32 target = 6; + inline bool has_target() const; + inline void clear_target(); + static const int kTargetFieldNumber = 6; + inline ::google::protobuf::uint32 target() const; + inline void set_target(::google::protobuf::uint32 value); + + // optional uint32 dataformat = 7; + inline bool has_dataformat() const; + inline void clear_dataformat(); + static const int kDataformatFieldNumber = 7; + inline ::google::protobuf::uint32 dataformat() const; + inline void set_dataformat(::google::protobuf::uint32 value); + + // optional uint64 glcontext = 8; + inline bool has_glcontext() const; + inline void clear_glcontext(); + static const int kGlcontextFieldNumber = 8; + inline ::google::protobuf::uint64 glcontext() const; + inline void set_glcontext(::google::protobuf::uint64 value); + + // optional bytes data = 9; + inline bool has_data() const; + inline void clear_data(); + static const int kDataFieldNumber = 9; + inline const ::std::string& data() const; + inline void set_data(const ::std::string& value); + inline void set_data(const char* value); + inline void set_data(const void* value, size_t size); + inline ::std::string* mutable_data(); + inline ::std::string* release_data(); + + // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.TexturePacket) + private: + inline void set_has_layerref(); + inline void clear_has_layerref(); + inline void set_has_width(); + inline void clear_has_width(); + inline void set_has_height(); + inline void clear_has_height(); + inline void set_has_stride(); + inline void clear_has_stride(); + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_target(); + inline void clear_has_target(); + inline void set_has_dataformat(); + inline void clear_has_dataformat(); + inline void set_has_glcontext(); + inline void clear_has_glcontext(); + inline void set_has_data(); + inline void clear_has_data(); + + ::google::protobuf::uint64 layerref_; + ::google::protobuf::uint32 width_; + ::google::protobuf::uint32 height_; + ::google::protobuf::uint32 stride_; + ::google::protobuf::uint32 name_; + ::google::protobuf::uint32 target_; + ::google::protobuf::uint32 dataformat_; + ::google::protobuf::uint64 glcontext_; + ::std::string* data_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(9 + 31) / 32]; + + friend void protobuf_AddDesc_LayerScopePacket_2eproto(); + friend void protobuf_AssignDesc_LayerScopePacket_2eproto(); + friend void protobuf_ShutdownFile_LayerScopePacket_2eproto(); + + void InitAsDefaultInstance(); + static TexturePacket* default_instance_; +}; +// ------------------------------------------------------------------- + +class Packet : public ::google::protobuf::MessageLite { + public: + Packet(); + virtual ~Packet(); + + Packet(const Packet& from); + + inline Packet& operator=(const Packet& from) { + CopyFrom(from); + return *this; + } + + static const Packet& default_instance(); + + void Swap(Packet* other); + + // implements Message ---------------------------------------------- + + Packet* New() const; + void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from); + void CopyFrom(const Packet& from); + void MergeFrom(const Packet& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::std::string GetTypeName() const; + + // nested types ---------------------------------------------------- + + typedef Packet_DataType DataType; + static const DataType FRAMESTART = Packet_DataType_FRAMESTART; + static const DataType FRAMEEND = Packet_DataType_FRAMEEND; + static const DataType COLOR = Packet_DataType_COLOR; + static const DataType TEXTURE = Packet_DataType_TEXTURE; + static inline bool DataType_IsValid(int value) { + return Packet_DataType_IsValid(value); + } + static const DataType DataType_MIN = + Packet_DataType_DataType_MIN; + static const DataType DataType_MAX = + Packet_DataType_DataType_MAX; + static const int DataType_ARRAYSIZE = + Packet_DataType_DataType_ARRAYSIZE; + + // accessors ------------------------------------------------------- + + // required .mozilla.layers.layerscope.Packet.DataType type = 1; + inline bool has_type() const; + inline void clear_type(); + static const int kTypeFieldNumber = 1; + inline ::mozilla::layers::layerscope::Packet_DataType type() const; + inline void set_type(::mozilla::layers::layerscope::Packet_DataType value); + + // optional .mozilla.layers.layerscope.FramePacket frame = 2; + inline bool has_frame() const; + inline void clear_frame(); + static const int kFrameFieldNumber = 2; + inline const ::mozilla::layers::layerscope::FramePacket& frame() const; + inline ::mozilla::layers::layerscope::FramePacket* mutable_frame(); + inline ::mozilla::layers::layerscope::FramePacket* release_frame(); + + // optional .mozilla.layers.layerscope.ColorPacket color = 3; + inline bool has_color() const; + inline void clear_color(); + static const int kColorFieldNumber = 3; + inline const ::mozilla::layers::layerscope::ColorPacket& color() const; + inline ::mozilla::layers::layerscope::ColorPacket* mutable_color(); + inline ::mozilla::layers::layerscope::ColorPacket* release_color(); + + // optional .mozilla.layers.layerscope.TexturePacket texture = 4; + inline bool has_texture() const; + inline void clear_texture(); + static const int kTextureFieldNumber = 4; + inline const ::mozilla::layers::layerscope::TexturePacket& texture() const; + inline ::mozilla::layers::layerscope::TexturePacket* mutable_texture(); + inline ::mozilla::layers::layerscope::TexturePacket* release_texture(); + + // @@protoc_insertion_point(class_scope:mozilla.layers.layerscope.Packet) + private: + inline void set_has_type(); + inline void clear_has_type(); + inline void set_has_frame(); + inline void clear_has_frame(); + inline void set_has_color(); + inline void clear_has_color(); + inline void set_has_texture(); + inline void clear_has_texture(); + + ::mozilla::layers::layerscope::FramePacket* frame_; + ::mozilla::layers::layerscope::ColorPacket* color_; + ::mozilla::layers::layerscope::TexturePacket* texture_; + int type_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + friend void protobuf_AddDesc_LayerScopePacket_2eproto(); + friend void protobuf_AssignDesc_LayerScopePacket_2eproto(); + friend void protobuf_ShutdownFile_LayerScopePacket_2eproto(); + + void InitAsDefaultInstance(); + static Packet* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +// FramePacket + +// optional uint64 value = 1; +inline bool FramePacket::has_value() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void FramePacket::set_has_value() { + _has_bits_[0] |= 0x00000001u; +} +inline void FramePacket::clear_has_value() { + _has_bits_[0] &= ~0x00000001u; +} +inline void FramePacket::clear_value() { + value_ = GOOGLE_ULONGLONG(0); + clear_has_value(); +} +inline ::google::protobuf::uint64 FramePacket::value() const { + return value_; +} +inline void FramePacket::set_value(::google::protobuf::uint64 value) { + set_has_value(); + value_ = value; +} + +// ------------------------------------------------------------------- + +// ColorPacket + +// required uint64 layerref = 1; +inline bool ColorPacket::has_layerref() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void ColorPacket::set_has_layerref() { + _has_bits_[0] |= 0x00000001u; +} +inline void ColorPacket::clear_has_layerref() { + _has_bits_[0] &= ~0x00000001u; +} +inline void ColorPacket::clear_layerref() { + layerref_ = GOOGLE_ULONGLONG(0); + clear_has_layerref(); +} +inline ::google::protobuf::uint64 ColorPacket::layerref() const { + return layerref_; +} +inline void ColorPacket::set_layerref(::google::protobuf::uint64 value) { + set_has_layerref(); + layerref_ = value; +} + +// optional uint32 width = 2; +inline bool ColorPacket::has_width() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void ColorPacket::set_has_width() { + _has_bits_[0] |= 0x00000002u; +} +inline void ColorPacket::clear_has_width() { + _has_bits_[0] &= ~0x00000002u; +} +inline void ColorPacket::clear_width() { + width_ = 0u; + clear_has_width(); +} +inline ::google::protobuf::uint32 ColorPacket::width() const { + return width_; +} +inline void ColorPacket::set_width(::google::protobuf::uint32 value) { + set_has_width(); + width_ = value; +} + +// optional uint32 height = 3; +inline bool ColorPacket::has_height() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void ColorPacket::set_has_height() { + _has_bits_[0] |= 0x00000004u; +} +inline void ColorPacket::clear_has_height() { + _has_bits_[0] &= ~0x00000004u; +} +inline void ColorPacket::clear_height() { + height_ = 0u; + clear_has_height(); +} +inline ::google::protobuf::uint32 ColorPacket::height() const { + return height_; +} +inline void ColorPacket::set_height(::google::protobuf::uint32 value) { + set_has_height(); + height_ = value; +} + +// optional uint32 color = 4; +inline bool ColorPacket::has_color() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void ColorPacket::set_has_color() { + _has_bits_[0] |= 0x00000008u; +} +inline void ColorPacket::clear_has_color() { + _has_bits_[0] &= ~0x00000008u; +} +inline void ColorPacket::clear_color() { + color_ = 0u; + clear_has_color(); +} +inline ::google::protobuf::uint32 ColorPacket::color() const { + return color_; +} +inline void ColorPacket::set_color(::google::protobuf::uint32 value) { + set_has_color(); + color_ = value; +} + +// ------------------------------------------------------------------- + +// TexturePacket + +// required uint64 layerref = 1; +inline bool TexturePacket::has_layerref() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void TexturePacket::set_has_layerref() { + _has_bits_[0] |= 0x00000001u; +} +inline void TexturePacket::clear_has_layerref() { + _has_bits_[0] &= ~0x00000001u; +} +inline void TexturePacket::clear_layerref() { + layerref_ = GOOGLE_ULONGLONG(0); + clear_has_layerref(); +} +inline ::google::protobuf::uint64 TexturePacket::layerref() const { + return layerref_; +} +inline void TexturePacket::set_layerref(::google::protobuf::uint64 value) { + set_has_layerref(); + layerref_ = value; +} + +// optional uint32 width = 2; +inline bool TexturePacket::has_width() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void TexturePacket::set_has_width() { + _has_bits_[0] |= 0x00000002u; +} +inline void TexturePacket::clear_has_width() { + _has_bits_[0] &= ~0x00000002u; +} +inline void TexturePacket::clear_width() { + width_ = 0u; + clear_has_width(); +} +inline ::google::protobuf::uint32 TexturePacket::width() const { + return width_; +} +inline void TexturePacket::set_width(::google::protobuf::uint32 value) { + set_has_width(); + width_ = value; +} + +// optional uint32 height = 3; +inline bool TexturePacket::has_height() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void TexturePacket::set_has_height() { + _has_bits_[0] |= 0x00000004u; +} +inline void TexturePacket::clear_has_height() { + _has_bits_[0] &= ~0x00000004u; +} +inline void TexturePacket::clear_height() { + height_ = 0u; + clear_has_height(); +} +inline ::google::protobuf::uint32 TexturePacket::height() const { + return height_; +} +inline void TexturePacket::set_height(::google::protobuf::uint32 value) { + set_has_height(); + height_ = value; +} + +// optional uint32 stride = 4; +inline bool TexturePacket::has_stride() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void TexturePacket::set_has_stride() { + _has_bits_[0] |= 0x00000008u; +} +inline void TexturePacket::clear_has_stride() { + _has_bits_[0] &= ~0x00000008u; +} +inline void TexturePacket::clear_stride() { + stride_ = 0u; + clear_has_stride(); +} +inline ::google::protobuf::uint32 TexturePacket::stride() const { + return stride_; +} +inline void TexturePacket::set_stride(::google::protobuf::uint32 value) { + set_has_stride(); + stride_ = value; +} + +// optional uint32 name = 5; +inline bool TexturePacket::has_name() const { + return (_has_bits_[0] & 0x00000010u) != 0; +} +inline void TexturePacket::set_has_name() { + _has_bits_[0] |= 0x00000010u; +} +inline void TexturePacket::clear_has_name() { + _has_bits_[0] &= ~0x00000010u; +} +inline void TexturePacket::clear_name() { + name_ = 0u; + clear_has_name(); +} +inline ::google::protobuf::uint32 TexturePacket::name() const { + return name_; +} +inline void TexturePacket::set_name(::google::protobuf::uint32 value) { + set_has_name(); + name_ = value; +} + +// optional uint32 target = 6; +inline bool TexturePacket::has_target() const { + return (_has_bits_[0] & 0x00000020u) != 0; +} +inline void TexturePacket::set_has_target() { + _has_bits_[0] |= 0x00000020u; +} +inline void TexturePacket::clear_has_target() { + _has_bits_[0] &= ~0x00000020u; +} +inline void TexturePacket::clear_target() { + target_ = 0u; + clear_has_target(); +} +inline ::google::protobuf::uint32 TexturePacket::target() const { + return target_; +} +inline void TexturePacket::set_target(::google::protobuf::uint32 value) { + set_has_target(); + target_ = value; +} + +// optional uint32 dataformat = 7; +inline bool TexturePacket::has_dataformat() const { + return (_has_bits_[0] & 0x00000040u) != 0; +} +inline void TexturePacket::set_has_dataformat() { + _has_bits_[0] |= 0x00000040u; +} +inline void TexturePacket::clear_has_dataformat() { + _has_bits_[0] &= ~0x00000040u; +} +inline void TexturePacket::clear_dataformat() { + dataformat_ = 0u; + clear_has_dataformat(); +} +inline ::google::protobuf::uint32 TexturePacket::dataformat() const { + return dataformat_; +} +inline void TexturePacket::set_dataformat(::google::protobuf::uint32 value) { + set_has_dataformat(); + dataformat_ = value; +} + +// optional uint64 glcontext = 8; +inline bool TexturePacket::has_glcontext() const { + return (_has_bits_[0] & 0x00000080u) != 0; +} +inline void TexturePacket::set_has_glcontext() { + _has_bits_[0] |= 0x00000080u; +} +inline void TexturePacket::clear_has_glcontext() { + _has_bits_[0] &= ~0x00000080u; +} +inline void TexturePacket::clear_glcontext() { + glcontext_ = GOOGLE_ULONGLONG(0); + clear_has_glcontext(); +} +inline ::google::protobuf::uint64 TexturePacket::glcontext() const { + return glcontext_; +} +inline void TexturePacket::set_glcontext(::google::protobuf::uint64 value) { + set_has_glcontext(); + glcontext_ = value; +} + +// optional bytes data = 9; +inline bool TexturePacket::has_data() const { + return (_has_bits_[0] & 0x00000100u) != 0; +} +inline void TexturePacket::set_has_data() { + _has_bits_[0] |= 0x00000100u; +} +inline void TexturePacket::clear_has_data() { + _has_bits_[0] &= ~0x00000100u; +} +inline void TexturePacket::clear_data() { + if (data_ != &::google::protobuf::internal::kEmptyString) { + data_->clear(); + } + clear_has_data(); +} +inline const ::std::string& TexturePacket::data() const { + return *data_; +} +inline void TexturePacket::set_data(const ::std::string& value) { + set_has_data(); + if (data_ == &::google::protobuf::internal::kEmptyString) { + data_ = new ::std::string; + } + data_->assign(value); +} +inline void TexturePacket::set_data(const char* value) { + set_has_data(); + if (data_ == &::google::protobuf::internal::kEmptyString) { + data_ = new ::std::string; + } + data_->assign(value); +} +inline void TexturePacket::set_data(const void* value, size_t size) { + set_has_data(); + if (data_ == &::google::protobuf::internal::kEmptyString) { + data_ = new ::std::string; + } + data_->assign(reinterpret_cast<const char*>(value), size); +} +inline ::std::string* TexturePacket::mutable_data() { + set_has_data(); + if (data_ == &::google::protobuf::internal::kEmptyString) { + data_ = new ::std::string; + } + return data_; +} +inline ::std::string* TexturePacket::release_data() { + clear_has_data(); + if (data_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = data_; + data_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} + +// ------------------------------------------------------------------- + +// Packet + +// required .mozilla.layers.layerscope.Packet.DataType type = 1; +inline bool Packet::has_type() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Packet::set_has_type() { + _has_bits_[0] |= 0x00000001u; +} +inline void Packet::clear_has_type() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Packet::clear_type() { + type_ = 1; + clear_has_type(); +} +inline ::mozilla::layers::layerscope::Packet_DataType Packet::type() const { + return static_cast< ::mozilla::layers::layerscope::Packet_DataType >(type_); +} +inline void Packet::set_type(::mozilla::layers::layerscope::Packet_DataType value) { + GOOGLE_DCHECK(::mozilla::layers::layerscope::Packet_DataType_IsValid(value)); + set_has_type(); + type_ = value; +} + +// optional .mozilla.layers.layerscope.FramePacket frame = 2; +inline bool Packet::has_frame() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void Packet::set_has_frame() { + _has_bits_[0] |= 0x00000002u; +} +inline void Packet::clear_has_frame() { + _has_bits_[0] &= ~0x00000002u; +} +inline void Packet::clear_frame() { + if (frame_ != NULL) frame_->::mozilla::layers::layerscope::FramePacket::Clear(); + clear_has_frame(); +} +inline const ::mozilla::layers::layerscope::FramePacket& Packet::frame() const { + return frame_ != NULL ? *frame_ : *default_instance_->frame_; +} +inline ::mozilla::layers::layerscope::FramePacket* Packet::mutable_frame() { + set_has_frame(); + if (frame_ == NULL) frame_ = new ::mozilla::layers::layerscope::FramePacket; + return frame_; +} +inline ::mozilla::layers::layerscope::FramePacket* Packet::release_frame() { + clear_has_frame(); + ::mozilla::layers::layerscope::FramePacket* temp = frame_; + frame_ = NULL; + return temp; +} + +// optional .mozilla.layers.layerscope.ColorPacket color = 3; +inline bool Packet::has_color() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void Packet::set_has_color() { + _has_bits_[0] |= 0x00000004u; +} +inline void Packet::clear_has_color() { + _has_bits_[0] &= ~0x00000004u; +} +inline void Packet::clear_color() { + if (color_ != NULL) color_->::mozilla::layers::layerscope::ColorPacket::Clear(); + clear_has_color(); +} +inline const ::mozilla::layers::layerscope::ColorPacket& Packet::color() const { + return color_ != NULL ? *color_ : *default_instance_->color_; +} +inline ::mozilla::layers::layerscope::ColorPacket* Packet::mutable_color() { + set_has_color(); + if (color_ == NULL) color_ = new ::mozilla::layers::layerscope::ColorPacket; + return color_; +} +inline ::mozilla::layers::layerscope::ColorPacket* Packet::release_color() { + clear_has_color(); + ::mozilla::layers::layerscope::ColorPacket* temp = color_; + color_ = NULL; + return temp; +} + +// optional .mozilla.layers.layerscope.TexturePacket texture = 4; +inline bool Packet::has_texture() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void Packet::set_has_texture() { + _has_bits_[0] |= 0x00000008u; +} +inline void Packet::clear_has_texture() { + _has_bits_[0] &= ~0x00000008u; +} +inline void Packet::clear_texture() { + if (texture_ != NULL) texture_->::mozilla::layers::layerscope::TexturePacket::Clear(); + clear_has_texture(); +} +inline const ::mozilla::layers::layerscope::TexturePacket& Packet::texture() const { + return texture_ != NULL ? *texture_ : *default_instance_->texture_; +} +inline ::mozilla::layers::layerscope::TexturePacket* Packet::mutable_texture() { + set_has_texture(); + if (texture_ == NULL) texture_ = new ::mozilla::layers::layerscope::TexturePacket; + return texture_; +} +inline ::mozilla::layers::layerscope::TexturePacket* Packet::release_texture() { + clear_has_texture(); + ::mozilla::layers::layerscope::TexturePacket* temp = texture_; + texture_ = NULL; + return temp; +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace layerscope +} // namespace layers +} // namespace mozilla + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_LayerScopePacket_2eproto__INCLUDED
new file mode 100644 --- /dev/null +++ b/gfx/layers/protobuf/LayerScopePacket.proto @@ -0,0 +1,40 @@ +option optimize_for = LITE_RUNTIME; + +package mozilla.layers.layerscope; + +message FramePacket { + optional uint64 value = 1; +} + +message ColorPacket { + required uint64 layerref = 1; + optional uint32 width = 2; + optional uint32 height = 3; + optional uint32 color = 4; +} + +message TexturePacket { + required uint64 layerref = 1; + optional uint32 width = 2; + optional uint32 height = 3; + optional uint32 stride = 4; + optional uint32 name = 5; + optional uint32 target = 6; + optional uint32 dataformat = 7; + optional uint64 glcontext = 8; + optional bytes data = 9; +} + +message Packet { + enum DataType { + FRAMESTART = 1; + FRAMEEND = 2; + COLOR = 3; + TEXTURE = 4; + } + required DataType type = 1; + + optional FramePacket frame = 2; + optional ColorPacket color = 3; + optional TexturePacket texture = 4; +}
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp +++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp @@ -42,16 +42,29 @@ protected: class APZCTreeManagerTester : public ::testing::Test { protected: virtual void SetUp() { gfxPrefs::GetSingleton(); AsyncPanZoomController::SetThreadAssertionsEnabled(false); } }; +class TouchActionEnabledTester : public AsyncPanZoomControllerTester { +protected: + virtual void SetUp() { + AsyncPanZoomControllerTester::SetUp(); + gfxPrefs::SetTouchActionEnabled(true); + } + + virtual void TearDown() { + gfxPrefs::SetTouchActionEnabled(false); + AsyncPanZoomControllerTester::TearDown(); + } +}; + class MockContentController : public GeckoContentController { public: MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&)); MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration)); MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&)); MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&)); MOCK_METHOD3(HandleLongTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&)); MOCK_METHOD3(HandleLongTapUp, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&)); @@ -142,25 +155,16 @@ class TestAPZCContainerLayer : public Co class TestAsyncPanZoomController : public AsyncPanZoomController { public: TestAsyncPanZoomController(uint64_t aLayersId, MockContentController* aMcc, APZCTreeManager* aTreeManager = nullptr, GestureBehavior aBehavior = DEFAULT_GESTURES) : AsyncPanZoomController(aLayersId, aTreeManager, aMcc, aBehavior) {} - // Since touch-action-enabled property is global - setting it for each test - // separately isn't safe from the concurrency point of view. To make tests - // run concurrent and independent from each other we have a member variable - // mTouchActionEnabled for each apzc and setter defined here. - void SetTouchActionEnabled(const bool touchActionEnabled) { - ReentrantMonitorAutoEnter lock(mMonitor); - mTouchActionPropertyEnabled = touchActionEnabled; - } - void SetFrameMetrics(const FrameMetrics& metrics) { ReentrantMonitorAutoEnter lock(mMonitor); mFrameMetrics = metrics; } FrameMetrics GetFrameMetrics() { ReentrantMonitorAutoEnter lock(mMonitor); return mFrameMetrics; @@ -275,26 +279,25 @@ void ApzcPan(AsyncPanZoomController* apz status = apzc->ReceiveInputEvent(mti); // Since we've explicitly built the overscroll handoff chain before // touch-start, we need to explicitly clear it after touch-end. aTreeManager->ClearOverscrollHandoffChain(); } static -void DoPanTest(bool aShouldTriggerScroll, bool aShouldUseTouchAction, uint32_t aBehavior) +void DoPanTest(bool aShouldTriggerScroll, uint32_t aBehavior) { TimeStamp testStartTime = TimeStamp::Now(); AsyncPanZoomController::SetFrameTime(testStartTime); nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager(); nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm); - apzc->SetTouchActionEnabled(aShouldUseTouchAction); apzc->SetFrameMetrics(TestFrameMetrics()); apzc->NotifyLayersUpdated(TestFrameMetrics(), true); if (aShouldTriggerScroll) { EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1)); EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); } else { EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(0); @@ -446,22 +449,16 @@ void DoPinchTest(bool aUseGestureRecogni if (aShouldTriggerPinch) { EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1)); EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); } else { EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtMost(2)); EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0); } - if (aAllowedTouchBehaviors) { - apzc->SetTouchActionEnabled(true); - } else { - apzc->SetTouchActionEnabled(false); - } - int touchInputId = 0; if (aUseGestureRecognizer) { ApzcPinchWithTouchMoveInput(apzc, 250, 300, 1.25, touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors); } else { ApzcPinchWithPinchInput(apzc, 250, 300, 1.25, aShouldTriggerPinch, aAllowedTouchBehaviors); } fm = apzc->GetFrameMetrics(); @@ -546,56 +543,56 @@ TEST_F(AsyncPanZoomControllerTester, Con nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc); apzc->SetFrameMetrics(TestFrameMetrics()); } TEST_F(AsyncPanZoomControllerTester, Pinch_DefaultGestures_NoTouchAction) { DoPinchTest(false, true); } -TEST_F(AsyncPanZoomControllerTester, Pinch_DefaultGestures_TouchActionNone) {